Php 保护无会话RESTful API端点的安全

Php 保护无会话RESTful API端点的安全,php,api,rest,curl,restful-authentication,Php,Api,Rest,Curl,Restful Authentication,我已经为下面的一个项目创建了一个简单的RESTful API。现在,我已经阅读了几十个关于堆栈溢出的类似问题,但我似乎找不到安全问题的答案 简而言之,我的要求如下: 客户端有一个公共API密钥(纯文本,任何嗅探网络流量或正确检查代码源的人都可以访问) 客户端使用公共API密钥向服务器发送请求 服务器有一个秘密的API密钥(除了开发人员之外,对任何人都是秘密的) 服务器创建一个HMAC-SHA1散列,该散列由客户端的请求数据和机密API密钥组成 服务器向API服务器发送一个与客户端请求相同的请求,

我已经为下面的一个项目创建了一个简单的RESTful API。现在,我已经阅读了几十个关于堆栈溢出的类似问题,但我似乎找不到安全问题的答案

简而言之,我的要求如下:

  • 客户端有一个公共API密钥(纯文本,任何嗅探网络流量或正确检查代码源的人都可以访问)
  • 客户端使用公共API密钥向服务器发送请求
  • 服务器有一个秘密的API密钥(除了开发人员之外,对任何人都是秘密的)
  • 服务器创建一个HMAC-SHA1散列,该散列由客户端的请求数据和机密API密钥组成
  • 服务器向API服务器发送一个与客户端请求相同的请求,但包括生成的HMAC-SHA1
  • API服务器根据接收到的公共API密钥在其数据库中查找机密API密钥
  • API服务器使用与开发人员服务器相同的数据重新创建HMAC-SHA1哈希
  • 如果哈希匹配,则认为请求有效,并正常处理
  • 我担心使用我的服务的人可能会获取公共API密钥(比如通过嗅探网络流量),然后简单地将客户端最初使用AJAX通过浏览器执行的相同请求直接卷曲到开发人员的服务器。因此,恶意用户可以作为合法用户进行身份验证,并使用其他人的API密钥访问API

    我将试着给出一个具体的例子。通常我会:

  • AJAX向我的服务器发送get请求
  • 我的服务器使用我的API机密对我的请求进行哈希处理,并将其发送到API服务器
  • API服务器验证我的请求并返回有效负载
  • 但我害怕:

  • 邪恶博士会嗅探我的公开API密钥
  • 邪恶博士将使用我的公共API密钥向我的服务器提交get请求
  • 我的服务器将使用我的API机密哈希邪恶博士的请求,并将其发送到API服务器
  • API服务器验证并返回有效负载,以完成邪恶博士的恶意计划
  • 邪恶博士笑出了邪恶的笑声
  • 我遗漏了什么,或者这只是RESTful API游戏的一部分

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

    更新2:我已经在流程中添加了一个
    $\u服务器['HTTP\u REFERER']
    验证。这里的目标是,客户机必须随请求一起发送一个referer,并且它必须匹配API端数据库中列出的referer。不幸的是,HTTP引用很容易被伪造。这是另一个安全级别,但仍不完美

    更新3:我已经更改了服务器端代码,将referer设置为远程IP地址。这将强制发送到我的服务器的每个请求(希望使用机密API密钥进行哈希处理)最终以原始请求IP地址到达API服务器。然后可以验证该IP,请求也可以通过。我相信仍然有可能伪造
    $\u服务器['REMOTE\u ADDR']
    ,但这比伪造
    $\u服务器['HTTP\u REFERER']
    更复杂。。。我想还是不完美


    更新4:根据这些帖子:而且,假装
    $\u服务器['REMOTE\u ADDR']
    是可能的,尽管很困难。但是,不可能从伪造的请求接收响应,因为您无法控制伪造的网络。请求可以成功验证,但其响应不会落入恶意用户之手。

    您使用HMAC的方法是正确的。但是,还有两件事可以使您的应用程序更加安全

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

  • 我发现的阻止其他脚本使用公共API密钥并向服务器端HMAC哈希脚本发送请求的解决方案是将原始请求者的身份与请求一起发送。我正在使用
    $\u SERVER['REMOTE\u 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密钥,还会检查
    $\u服务器['HTTP\u REFERER']
    是否被列为允许!这还允许API根据每个用户接受服务器。

    谢谢。我自愿排除了时间戳部分以保持简单,因为在我当前的代码中实现它并没有什么大不了的,我应该提到这一点。我不确定我是否正确理解SSL的功能。这会使交通嗅探变得不可能吗?恶意用户仍然可以从JavaScript源代码中窃取它,不是吗?我肯定遗漏了一些东西…SSL是传输加密。这使得嗅探更加困难。即使使用API,这也是一个好主意。但如果恶意用户可以从源代码处获取公共API密钥,我仍然会遇到同样的问题,对吗?关键在于密钥,因为它用于在HMAC中签名。公钥用于在服务器端查找机密。我不太清楚你的问题是什么意思。情况是这样的:恶意用户检查客户端JavaScript,找出公共API密钥和PHP脚本的路径,我发送它来用我的秘密API密钥散列请求。然后,恶意用户继续创建他或她自己的PHP脚本,以卷曲到我的PHP脚本,该脚本使用我的机密API密钥对请求进行哈希处理。恶意用户现在可以使用我的公共API密钥向他或她的PHP脚本发送请求,并使用他或她的数据,然后将其卷曲到我的服务器,使用我的机密API密钥进行哈希运算,并且仍然有效