2013-01-14 88 views
5

我已经为部分遵循this very good blog post by Riyad Kalla的项目创建了一个简单的RESTful API。现在,我已经阅读了堆栈溢出的几十个类似问题,但我似乎无法找到我的安全问题的答案。保护无会话RESTful API端点

简单地说,我的要求是这样的:

  1. 客户端有一个公共的API密钥(纯文本,任何人嗅探网络流量或正确地检查代码源访问)
  2. 客户端发送对服务器的请求,与公众的API密钥
  3. 该服务器有一个秘密的API密钥(秘密到任何人,但开发者)
  4. 服务器创建客户端的请求数据组成的HMAC-SHA1散列和秘密API密钥
  5. 服务器发送相同的客户端的请求,API服务器的请求,但其中所产生的HMAC-SHA1
  6. 的API服务器查找基于公共API密钥收到
  7. 在其数据库中的秘密API密钥API服务器重新创建使用作为开发者的服务器相同的数据
  8. 如果哈希匹配,该请求被认为是有效的,并且通常处理

我担心用我的服务了别人HMAC-SHA1哈希可能获取公共API密钥(通过嗅探网络流量),然后简单地卷曲sam e请求客户最初通过使用AJAX的浏览器直接向开发人员的服务器完成。因此,恶意用户可以被认证为合法用户并使用其他人的秘密API密钥访问API。

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

  1. AJAX a get request to my server。
  2. 我的服务器用我的API密钥散列我的请求并将它发送到API服务器。
  3. API服务器验证我的请求并返回有效负载。

但我很害怕,:

  1. 邪恶博士将嗅出我的公开API密钥。
  2. Dr. Evil将使用我的公共API密钥向我的服务器发送请求。
  3. 我的服务器会将Dr. Evil的请求与我的API秘密一起打散并发送给API服务器。
  4. API服务器验证并返回有效载荷以完成Dr. Evil的恶意计划。
  5. Dr. Evil笑一个邪恶的笑。

我什么都不知道,或者这只是RESTful API游戏的一部分?

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

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

更新3:我已更改我的服务器端代码以将引用链接设置为远程IP地址。这会强制发送到我的服务器的每个请求都要使用秘密API密钥进行散列,最终会使用原始请求IP地址访问API服务器。这个IP然后可以被验证并且请求可以通过。我相信它还有可能伪造$_SERVER['REMOTE_ADDR'],但它比假冒$_SERVER['HTTP_REFERER']更复杂...我猜还是不完美。

UPDATE 4:根据这些帖子:How to fake $_SERVER['REMOTE_ADDR'] variable?https://serverfault.com/questions/90725/are-ip-addresses-trivial-to-forge,伪造的$_SERVER['REMOTE_ADDR']是可能的,虽然困难。但是,由于您无法控制伪造的网络,因此无法收到来自伪造请求的回复。这个请求可以被成功验证,但是它的回应不会落入恶意的手中。

回答

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 
)); 

一旦发送出去,该API不仅检查从数据库中秘密API密钥,但它还会检查$_SERVER['HTTP_REFERER']被列为不允许的!这也允许API在每个用户的基础上接受服务器。

4

您正在通过使用HMAC正确的轨道。但是,还有两件事会让你的应用程序更安全。

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

谢谢。我主动排除时间戳部分以使事情变得简单,因为在我当前的代码中实现并不是什么大事,我应该提到这一点。我不确定我是否正确理解SSL的功能。它是否使交通嗅探不可能?恶意用户仍然可以从JavaScript源代码中窃取它,不是吗?我绝对错过了这里的东西...... –

+0

SSL是传输加密。它使得嗅探更加困难。即使使用API​​,这也是一个好主意。 – datasage

+0

但是如果恶意用户可以从源头获取公共API密钥,我仍然会遇到同样的问题,对吧? –