问题 创建PHP API:检查API请求来自哪个服务器


我正在为网站创建一个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。由于共享主机(它们具有相同的IP),检查_SERVER [“REMOTE_HOST”]实际上不是一个选项,因此这将无法防止滥用(无论如何主要来自共享服务器)。

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


3591
2017-08-10 08:01


起源

这可能是有趣的: phpweby.com/software/ip2country - James P.


答案:


@Shafee有一个好主意,只需要一些调整。我们专注于API调用的可见部分,即API密钥。这在URL中可见并告诉API  正在请求数据。而不是试图阻止其他人窃取此密钥并使用他们截获的域运行他们自己的cURL调用,我们 能够 '只需添加'混合的另一个关键,这个拦截器不可见。我不是说停止检查请求来自何处,它仍然是在脚本早期发出无效请求的好方法,但是使用第二个密钥,您保证只有请求数据的人才知道如何获取数据(你相信他们不会把它交给任何人)。

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

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


7
2017-08-10 18:56



+1为深入解释..:D - shxfee
file_get_contents的用法 是 大多数共享托管服务器存在问题,因此我们避免使用它。无论如何,cURL是连接到大多数基于Web的(HTTP)API的正确方法(套接字除外)。 file_get_contents是一个肮脏的黑客。 :p - Jimmie Lin


你可以改变请求来自哪个ip,你可以做一个ptr搜索来获取该ip的域名,但是知道ip地址有多个域名,你最终会错误的,所以我推荐客户端在请求中发送他的域名,也许是HTTP_REFERER,并且你做了一个dns检查该域是否指向ip要求它,但是注意一个域,比如google.com,可以指向多个ip 。 (知识产权可能会被伪装成一些好的黑客技能,但据我所知)


3
2017-08-10 08:23



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


如何引入第二个变量就像让我们说的那样 app id。当用户注册其域时,将此id与域关联。用户需要提交 app id 每个请求都没有加密以及加密的api调用。那你就可以抬头了 app id 得到的 app secret 并尝试解密?


3
2017-08-10 08:24



那么那就首先打破了拥有API密钥的目的,抱歉。我正在使用API​​密钥(并检查要匹配的域)以确保没有滥用。在这种情况下,我可以假装AppID和APIKey,就像我用REFERER解决方案指出的那样。谢谢! :) - Jimmie Lin


为了最好地防止滥用您的API,请限制请求的速度,或限制他们可以提出的请求数量。如果某人是愚蠢的并且共享他们的API密钥,他们将只限制他们自己的API使用,使那些打算滥用API获取他们自己的密钥的人更经济。

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

此外,您可以组合限制速度/限制请求,并根据请求数限制速度,例如,如果您传递一定数量的数据,Verizon将如何限制其3G网络的速度。


2
2017-08-10 16:32



滥用不仅仅是这样。如果共享API密钥,则安全性会受到影响。这是另一个需要担心的主要问题。 - Jimmie Lin