如何使用公钥和私钥保护小型 php api

2022-08-30 23:48:09

我正在尝试设计一个小的api,但是我有点卡在如何保护api上。我已经阅读了一些关于如何做到这一点的文章,其中之一是:登录并检索apikey,然后使用此apikey对一些值进行哈希处理,并将哈希字符串与请求一起发送回去,因此可以在服务器级别再次完成。

这是一个好方法还是这样做很危险?

如果没有错过理解,为了避免中间的人,我可以将请求URL添加到将要散列的变量中,或者这不是适当的方式

此外,我的大脑被困在如何使用时间戳上,以避免对具有相同数据的相同URL发出大量请求。

如果我的问题被问了1000次,我很抱歉。然而,我现在已经阅读了一些文章,我仍然不清楚我的小api该走什么路。

从我所读到的和理解中,这应该是一条路。

  1. 公钥存储在应用程序中,以允许用户或应用程序登录。
  2. 服务器在访问此特定用户时为其创建私钥。或者这应该始终是相同的,还是由一个人创建的静态值?
  3. 用户发出请求,随请求一起发送hash_hmac签名(某些值+私钥);
  4. 服务器检查这些值是否正确,并通过从发送的值创建相同的哈希值来执行此操作。
  5. 如果服务器生成相同的哈希,则请求有效,然后可以执行。
  6. 这是他们要走的路,还是我在这里错过了一些市长的事情。

对于拥有数据的人来说,下面的方式是创建哈希的好方法吗?

$l_sPrivateKey = 'something returned by database when user loged in';
$l_aData = array();

foreach($_POST as $key => $value){
 if($key == 'signature') continue;
 $l_aData[$key] = $value;
}

//This should then be the same as $_POST['signature'];
hash_hmac('sha256',serialize($l_aData),$l_sPrivateKey, false); 

您的意见将不胜感激。提前亲切的问候和感谢


答案 1

具有 HMAC 的安全远程密码协议 (SRP6a) 符合您的要求

以下假设您的 API 是浏览器到服务器的,因此 JavaScript 到 PHP 不是仅使用 PHP 的服务器到服务器。SRP 将同时适用于这两种方案,但下面的答案讨论了浏览器到服务器库。

使用安全远程密码协议对 API 的用户进行身份验证,这会产生创建强会话密钥的副作用。然后,您可以使用共享的强会话密钥,通过 HMAC 对 API 请求和响应进行签名。

RFC5054 使用 SRP 而不是公钥来创建共享会话密钥来加密 TLS 流量。OpenSSL中有一个实现。这表明 SRP 身份验证是完全安全的公钥替代品,可以创建安全的共享密钥。恕我直言,使用SRP可以更方便地解决您的问题。

Thinbus SRP库是一个JavaScript SRP库,它具有对PHP服务器进行身份验证的演示。PHP 演示没有显示使用共享会话密钥,但一旦身份验证协议完成,它只是在服务器和浏览器中。默认的 Thinbus 配置会产生 256 位共享密钥。您可以将此功能与 HMAC 一起使用,请参阅下面有关使用签名 JSON 的脚注 1。$srp->getSessionKey()client.getSessionKey()

它是如何运作的

注册流程为:

  1. 客户端 API 注册表单在客户端使用 JavaScript 生成一个随机 API 密码,该密码不会传输到服务器。这被保存到浏览器本地存储中,并显示给用户,要求他们打印出来并保留备份。
  2. 密码被提供给 Thinbus SRP 客户端 JS 库代码,该代码输出客户端盐和密码验证器。
  3. 盐和验证器将发布到服务器并保存在该客户端的数据库中。通常,Thinbus建议您通过使用HTTPS将验证程序发送到服务器来隐藏验证程序,以防止暴力攻击来恢复密码。如果您使用的是随机生成的密码,只要是典型的软件许可证密钥,那么您可以通过HTTP传输验证程序。见下文脚注2。

API 使用流将从客户端的 SRP 身份验证开始,该身份验证具有生成会话密钥的副作用。请注意,所有这些都在 Thinbus 演示代码中作为“标准用法”进行,但此处进行了解释,以提供 STP 身份验证的工作原理。此身份验证协议显示在 thinbus 页面的序列图中,并在在线演示中运行:

  1. 客户端 javascript 从浏览器本地存储加载 API 密码。
  2. 客户端 AJAX 从服务器获取客户端盐和服务器随机一次性编号。B
  3. 客户端javascript生成一个一次性数字,然后使用密码,salt和两个一次性数字来生成会话密钥,并使用两个一次性数字进行哈希处理,以创建密码证明,该密码证明与其随机一起发布到服务器。AKMA
  4. 服务器使用注册时保存到数据库的密码验证器、客户端盐和两个随机数来计算会话密钥,然后确认客户端发送的密码证明是否良好。如果这一切都很好,它会将自己的证据发回给客户端。此时,客户端已使用 STP 作为密码的零知识证明进行身份验证。KMM2
  5. 客户端根据其计算进行检查。如果一切正常,双方都有一个共享的秘密,这是一个从随机派生的一次性256位会话密钥,没有中间人可以知道。M2KAB
  6. 所有 API 请求和响应都可以使用共享密钥进行 HMAC 签名,并在另一端进行验证。

以上所有内容都在Thinbus的PHP演示中进行了介绍,减去最后实际调用以拥有一个可用于使用HMAC对事物进行签名的密钥。$srp->getSessionKey()

鉴于SRP将密码身份验证替换为密码的加密零知识证明,令人惊讶的是,默认情况下并非所有开发人员都使用它。它还为API签名生成共享会话密钥的事实只是一个额外的好处。


脚注1:大多数 API 更愿意发布一个包含所有数据的 JSON 值。这是因为JSON很简单,但功能更强大,在PHP和JavaScript中内置了API,可以将对象转换为字符串并再次返回。正如@dbrumann在注释中指出的那样,有一个用于签署JSON的标准,即JWT。Google建议这里有PHP和JavaScript的库。因此,如果您升级到传递一个 JSON 输入值并为 API 中的每个命令返回一个 JSON 输出,则可以使用 JWT 库对 API 的 JSON 输入和输出进行签名和验证。JWS算法之一是“JWSAlgorithm.HS256 - HMAC with SHA-256, 256+ bit secret”。这些库将整理实际签名和验证的机制,因此您不必编写该代码并担心可能的安全错误。

脚注2:Thinbus的建议是通过HTTPS将密码验证器传输到服务器,以保持验证程序的机密性。这是为了防止拦截然后对密码验证器的离线字典攻击以恢复密码(即密码被盐渍到验证器中,因此您需要通过验证器生成代码运行16G破解密码字典与用户盐以找到匹配项)。通过API的使用,browser window.crypto API可以生成一个真正随机的“API密钥”。通常,Windows 键是 16 个大写字母,以格式设置为 XXXX-XXXX-XXXX-XXXX。检查GRC密码搜索空间页面,它说随机的16个字母大写密码的大小需要政府14年才能彻底搜索。鉴于这种估计,您可以在没有加密的纯HTTP上安全地传输为如此长的随机密码生成的密码验证器,因为没有人会通过验证器生成算法(使用随机客户端盐,因此无法预先计算)来找到匹配项来恢复客户端API密码。


答案 2

推荐