【问题标题】:Creating a PHP API: Checking from which server the API Request comes from创建 PHP API:检查 API 请求来自哪个服务器
【发布时间】:2011-10-23 20:52:17
【问题描述】:

我正在为网站创建一个 PHP API,我想限制 API 访问在我们服务器上注册的域(以防止滥用 API 使用)。所以,这就是我现在的方法,嗯,它在纸上看起来应该不错。

  1. API 设置在api.example.com
  2. 想要使用 API 的用户向我们注册,添加他的域并获得 API 密钥。
  3. API 的用户将使用他的 API 密钥来加密他的请求数据(通过mcrypt)并通过cURL 将其发送到api.example.com
  4. 我的服务器会检查此 API 请求来自哪个域,并将该域与数据库中的 API 密钥进行匹配。如果有 API 密钥,API 通过 mcrypt 使用该密钥解密请求,然后使用相同的方法加密并发送结果。

我被困在第 4 步。最初,我计划使用 HTTP_REFERER 来检查它,但由于 cURL 默认情况下不发送它,并且它很容易在用户端代码中伪造(CURLOPT_REFERER 就我而言记住),我被困在这里了。

有没有办法知道这个 API 请求来自哪个域?我看到它可以使用一些流行的 API 来完成,比如 reCAPTCHA 之一。检查 _SERVER["REMOTE_HOST"] 并不是一个真正的选择,因为共享主机(它们具有相同的 IP),因此这将无法防止滥用(无论如何这主要来自共享服务器)。

有没有这样的方法来检查它?谢谢!

【问题讨论】:

标签: php api curl dns request


【解决方案1】:

@Shafee 有一个好主意,它只是需要一些调整。我们专注于 API 调用的可见部分,即 API 密钥。这在 URL 中可见,并告诉 API 正在请求数据。与其试图阻止其他人窃取此密钥并使用他们从中拦截它的域运行他们自己的 cURL 调用,我们可以“只是添加”另一个密钥,这个密钥对那些拦截器不可见.我并不是说停止检查请求的来源,这仍然是在脚本早期排除无效请求的好方法,但是使用第二个键,您可以保证只有请求数据的人才知道如何获取数据(您相信他们不会将其泄露给任何人)。

因此,当用户注册一个密钥时,您实际上是为该用户分配了两个不同的密钥。
API_KEY - 将您连接到您的域的公钥。系统查找提供的域和密钥以找到下一个密钥。
MCRYPT_KEY - 这是用于通过 Mcrypt 实际加密该数据的密钥。由于它是加密数据,只有请求者和服务器才能知道它是什么。您使用密钥来加密数据并将加密的输入与您的 API 密钥一起发送到服务器,服务器会通过提供的 API 密钥和域(和 IP)找到解密该输入所需的密钥。如果他们没有使用正确的密钥加密数据,那么使用正确的密钥解密将返回乱码,json_decode() 调用将返回 NULL,从而允许脚本简单地返回“invalid_input”响应。

最终使用这种方法,我们是否甚至需要检查请求来自哪里(域/IP)?使用这种方法实际上归结为 API 用户不会将他们的 API/MCRYPT 密钥对泄露给其他用户,类似于不泄露您的用户名/密码。即便如此,任何网站都可以轻松地注册以获取自己的密钥对并使用 API。还要注意的是,除非用户使用正确的用户名和密码登录,否则 API 甚至不会向他们的服务器返回任何有用的信息,因此他们的终端已经拥有该信息。我们的服务器真正返回的唯一新内容是成功验证用户后的电子邮件地址。话虽如此,我们甚至需要使用 cURL 吗?难道我们不能简单地使用file_get_contents('http://api.example.com/{$API_KEY}/{$MCRYPT_DATA}')吗?我意识到我在回答中提出了更多问题......

【讨论】:

  • file_get_contents 的使用在大多数共享主机服务器中的一个问题,因此我们避免使用它。无论如何,cURL 是连接到大多数基于 Web 的 (HTTP) API 的正确方式(套接字除外)。 file_get_contents 是一个肮脏的黑客。 :p
【解决方案2】:

您可以验证请求来自哪个 ip,并且您通常可以进行 ptr 搜索以获取该 ip 的域名,但探测该 ip 地址有多个域,您最终会选择错误的一个,所以我建议客户端在请求中发送他的域名,也许是 HTTP_REFERER,并且你做一个 dns 检查该域是否指向请求它的 ip,但请注意,像 google.com 这样的域可以指向更多然后一个ip。 (ip 可能会被伪造,具有一些好的黑客技能,但那是我所不知道的)

【讨论】:

  • 所以你的意思是注册一个 IP/APIKey,然后反向查找 IP 到它的域,将域(通过 HTTP_REFERER 发送)与域列表匹配,如果匹配,就去吧?这也是一个解决方案,是的,但它有点复杂。谢谢! :)
  • 我看不出这是如何解决问题的。存在同样的问题。多个网站可以从一个 IP 运行。因此,如果您只是使用 IP 来查找托管在该 IP 地址上的所有域并检查提供的域是否在该列表中,这与仅检查该 IP 是否与给定的 API 密钥有什么不同?服务器上的任何人仍然可以通过在 cURL 调用中指定该域来调用 API。
  • 一个域名只是一个IP地址的别名,如果多个域名指向同一个IP地址,就像我有多个昵称,如果你叫我“puggan” ”、“tobbe”或“thorbjörn”,我可能会有不同的反应,但如果我打电话给你,你看不出是“puggan”、“tobbe”还是“thorbjörn”,他们都是我
  • 更好的域名和 IP 扩展是住宅地址。房子里可能住着 4 个人,其中几个人的昵称也可能指向那个地址,但最终还是有 4 个不同的人住在那里。您的示例会假设指向单个 IP 的所有域都归同一个人所有,但情况可能并非如此。
  • 在我的例子中,我做了一个人=一台服务器,但正如你所说,服务器可以共享,但你永远看不到所有者连接到你,它连接到你的服务器,而你当服务器开始与您交谈时,不知道服务器上登录的用户是什么
【解决方案3】:

如何引入第二个变量,比如app id。当用户注册她的域时,将此 id 与域相关联。用户需要在每个请求中提交app id,无需加密以及加密的api调用。那你可以查app id 得到app secret 并尝试解密?

【讨论】:

  • 那将大大违背最初拥有 API 密钥的目的,抱歉。我正在使用 API 密钥(并检查域以匹配)以确保没有滥用。在这种情况下,可以像我在 REFERER 解决方案中指出的那样伪造 AppID 和 APIKey。不过谢谢! :)
【解决方案4】:

为了最好地防止 API 被滥用,请限制请求的速度,或限制它们可以发出的请求数量。如果某人很愚蠢并分享了他们的 API 密钥,他们只会限制他们自己的 API 使用,让那些打算滥用 API 的人获得他们自己的密钥更经济。

另外,如果有人决定使用您的 API 实现桌面应用程序怎么办?他们肯定不会要求他们的用户将他们的 IP 地址发送给他们以便他们可以将他们列入白名单吗?

此外,您可以结合限制速度/限制请求,并根据请求数量限制速度,例如,如果您超过一定的数据使用量,Verizon 如何限制其 3G 网络的速度。

【讨论】:

  • 滥用不只是这样。如果 API 密钥是共享的,安全性就会受到损害。这是另一个需要担心的主要问题。
猜你喜欢
  • 1970-01-01
  • 2021-05-24
  • 2012-05-08
  • 1970-01-01
  • 1970-01-01
  • 2016-02-15
  • 1970-01-01
  • 2019-03-08
  • 1970-01-01
相关资源
最近更新 更多