保护无会话的RESTful API端点

Rém*_*ton 5 php api rest curl restful-authentication

我为一个项目创建了一个简单的RESTful API,部分遵循了Riyad Kalla的这篇非常好的博客文章.现在,我已经在Stack Overflow上阅读了几十个类似的问题,但我似乎无法找到我的安全问题的答案.

简而言之,我的要求是这样的:

  1. 客户端有一个公共API密钥(纯文本,任何人都可以访问网络流量或正确检查代码源)
  2. 客户端使用公共API密钥向服务器发送请求
  3. 服务器有一个秘密的API密钥(除了开发人员之外的任何人都有秘密)
  4. 服务器创建由客户端的请求数据和秘密API密钥组成的HMAC-SHA1哈希
  5. 服务器向API服务器发送与客户端请求相同的请求,但包括生成的HMAC-SHA1
  6. API服务器根据它接收的公共API密钥在其数据库中查找秘密API密钥
  7. API服务器使用与开发人员服务器相同的数据重新创建HMAC-SHA1哈希
  8. 如果哈希匹配,则认为该请求有效并正常处理

我担心有人使用我的服务可以获取公共API密钥(通过嗅探网络流量),然后简单地通过他们的浏览器使用AJAX直接向开发人员的服务器发出相同的请求.因此,恶意用户可以被认证为合法用户并使用其他秘密API密钥访问API.

我会尝试举一个具体的例子.通常我会这样做:

  1. AJAX向我的服务器发出get请求.
  2. 我的服务器用我的API秘密哈希我的请求并将其发送到API服务器.
  3. API服务器验证我的请求并返回有效负载.

但我很害怕:

  1. Evil博士将嗅探我的公共API密钥.
  2. Evil博士将使用我的公共API密钥向我的服务器发出get请求.
  3. 我的服务器将使用我的API秘密来散布Dr. Evil的请求并将其发送到API服务器.
  4. API服务器验证并返回有效负载以完成Dr. Evil的恶意计划.
  5. 邪恶的博士笑了起来.

我还缺少什么,或者这只是RESTful API游戏的一部分?

更新:我自愿省略任何形式的时间戳验证,以保持简单,只关注身份验证问题.

更新2:我已经$_SERVER['HTTP_REFERER']为流程添加了验证.这里的目标是客户端必须与请求一起发送引用者,并且它必须与API侧数据库中列出的引用者匹配.不幸的是,HTTP引用可以很容易伪造.这是另一个安全级别,但仍然不完美.

更新3:我已经更改了服务器端代码,将引用者设置为远程IP地址.这会强制发送到我的服务器的每个请求使用秘密API密钥进行哈希处理,最终使用原始请求IP地址到达API服务器.然后可以验证此IP并且请求可以通过.我相信它还可以伪造$_SERVER['REMOTE_ADDR'],但它比假装更复杂$_SERVER['HTTP_REFERER']......我猜还是不完美.

更新4:根据这些帖子:如何伪造$ _SERVER ['REMOTE_ADDR']变量?https://serverfault.com/questions/90725/are-ip-addresses-trivial-to-forge,$_SERVER['REMOTE_ADDR']虽然很难做到伪造.但是,由于您无法控制伪造的网络,因此无法接收伪造请求的响应.该请求可以成功验证,但其响应不会落入恶意手中.

dat*_*age 5

通过使用 HMAC,您走在正确的轨道上。但是,还有两件事可以使您的应用程序更加安全。

  1. 在客户端 POST 中需要时间戳,需要在服务器时间的 5 分钟内验证。它也应该包含在 HMAC 生成中。如果有人试图改变这个 HMAC 签名将是无效的,除非他们有更新 HMAC 签名的密钥。
  2. 使用 SSL,并进行证书验证。防止中间人攻击。不允许任何非 SSL 请求。


Rém*_*ton 0

我发现阻止其他脚本使用公共 API 密钥并向服务器端 HMAC 哈希脚本发送请求的解决方案是随请求一起发送原始请求者的身份。我用来$_SERVER['REMOTE_ADDR']确定原始请求者的身份,因为它更难伪造,而伪造通常意味着他们不会得到回复。

/* $this as a class that handles requests */

// Build hash and include timestamp
$this->vars['timestamp'] = time();
$this->vars['hash'] = hash_hmac('sha1', http_build_query($this->vars).$this->vars['token'], API_SECRET);

// Send request to API
curl_setopt_array($this->curl, array(
    CURLOPT_RETURNTRANSFER => 1,
    CURLOPT_URL => $url,
    CURLOPT_POST => $this->method == 'post' ? 1 : NULL,
    CURLOPT_POSTFIELDS => $this->method == 'post' ? $this->vars : NULL,
    CURLOPT_CONNECTTIMEOUT => 15,
    CURLOPT_TIMEOUT => 15,
    CURLOPT_REFERER => $_SERVER['REMOTE_ADDR'], // Referer here!
    CURLOPT_MAXREDIRS => 3,
    CURLOPT_HTTPGET => $this->method == 'get' ? true : false
));
Run Code Online (Sandbox Code Playgroud)

发送后,API 不仅会检查数据库中的秘密 API 密钥,还会检查是否被$_SERVER['HTTP_REFERER']列为允许的!这还允许 API 接受基于每个用户的服务器。