5

我正在考虑一个休息网络服务,以确保发送给他的每个请求:

  • 该请求是由声称它的用户生成的;
  • 请求未被其他人修改(uri/method/content/date);
  • 对于 GET 请求,应该可以生成一个包含足够信息的 URI,以检查签名并设置到期日期。这样,用户可以在有限的时间段内将临时 READ 权限委托给协作者,使用生成的 URI 对资源进行访问。

客户端通过 id 和基于其密码的内容签名进行身份验证。

根本不应该有会话,所以服务器状态!服务器和客户端共享一个密钥(密码)

在考虑了它并与一些非常好的人交谈之后,似乎没有其他服务可以像我的用例那样简单地做到这一点。(HTTP Digest 和 OAuth 可以在服务器状态下做到这一点,而且非常健谈)

所以我想象了一个,我想请教各位大神对它应该如何设计的意见(我将发布它开源并希望它可以帮助其他人)。

该服务使用自定义“内容签名”标头来存储凭据。经过身份验证的请求应包含此标头:

Content-signature: <METHOD>-<USERID>-<SIGNATURE>

<METHOD> is the sign method used, in our case SRAS.
<USERID> stands for the user ID mentioned earlier.
<SIGNATURE> = SHA2(SHA2(<PASSWORD>):SHA2(<REQUEST_HASH>));
<REQUEST_HASH> = <HTTP_METHOD>\n
                 <HTTP_URI>\n
                 <REQUEST_DATE>\n
                 <BODY_CONTENT>;

请求在创建 10 分钟后失效。

例如,典型的 HTTP 请求将是:

POST /ressource HTTP/1.1
Host: www.elphia.fr
Date: Sun, 06 Nov 1994 08:49:37 GMT
Content-signature: SRAS-62ABCD651FD52614BC42FD-760FA9826BC654BC42FD

{ test: "yes" }

服务器会回答:

401 Unauthorized

或者

200 OK

变量将是:

<USERID> = 62ABCD651FD52614BC42FD
<REQUEST_HASH> = POST\n
                 /ressource\n
                 Sun, 06 Nov 1994 08:49:37 GMT\n
                 { test: "yes" }\n

URI 参数

可以将一些参数添加到 URI(它们使标头信息超载):

  • _sras.content-signature=<METHOD>-<USERID>-<SIGNATURE> :将凭证放在 URI 中,而不是 HTTP 标头中。这允许用户共享已签名的请求;
  • _sras.date=Sun, 06 Nov 1994 08:49:37 GMT(请求日期*):创建请求的日期。
  • _sras.expires=Sun, 06 Nov 1994 08:49:37 GMT (expire date*) : 告诉服务器请求不应在指定日期之前过期

*日期格式:http ://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.18

感谢您的意见。

4

2 回答 2

5

在设计签名协议时,您需要考虑几个问题。其中一些问题可能不适用于您的特定服务:

1-习惯上为非标准标题添加“X-Namespace-”前缀,在您的情况下,您可以将标题命名为:“X-SRAS-Content-Signature”。

2- Date 标头可能无法为 nonce 值提供足够的分辨率,因此我建议时间戳至少具有 1 毫秒的分辨率。

3- 如果您至少不存储最后一个 nonce,仍然可以在 10 分钟窗口内重播消息,这对于 POST 请求可能是不可接受的(可能在您的 REST Web 服务中创建具有相同值的多个实例)。对于 GET PUT 或 DELETE 动词,这应该不是问题。

但是,在 PUT 上,这可以通过在建议的 10 分钟窗口内强制更新同一对象多次来用于拒绝服务攻击。在 GET 或 DELETE 上存在类似的问题。

因此,您可能需要至少存储与每个用户 ID 关联的最后使用的 nonce,并在所有身份验证服务器之间实时共享此状态。

4- 此方法还要求客户端和服务器时钟同步,偏差小于 10 分钟。如果您有不控制时钟的 AJAX 客户端,这可能很难调试,或者无法强制执行。这也需要在 UTC 中设置所有时间戳。

另一种方法是放弃 10 分钟的窗口要求,但验证时间戳是否单调增加,这再次需要存储最后一个 nonce。如果客户端的时钟更新到上次使用的 nonce 之前的日期,这仍然是一个问题。访问将被拒绝,直到客户端的时钟经过最后一个 nonce 或服务器 nonce 状态被重置。

对于无法存储状态的客户端,单调递增计数器不是一个选项,除非客户端可以向服务器请求最后使用的 nonce。这将在每个会话开始时完成一次,然后计数器将在每次请求时递增。

5-您还需要注意由于网络错误导致的重传。您不能假设在 TCP 连接断开之前,服务器尚未收到客户端尚未收到 TCP Ack 的最后一条消息。因此,需要在 TCP 级别以上的每次重传和使用新的 nonce 重新计算签名之间增加 nonce。然而,需要添加消息编号以防止在服务器上重复执行:双 POST 将导致创建 2 个对象。

6- 您还需要对用户 ID 进行签名,否则,攻击者可能能够为所有未达到重播消息的 nonce 的用户重播相同的消息。

7-您的方法不能保证客户端服务器是真实的并且没有被 DNS 劫持。服务器身份验证通常被认为对安全通信很重要。可以通过使用与请求相同的随机数对来自服务器的响应进行签名来提供此服务。

于 2010-12-26T20:27:12.747 回答
1

我会注意到您可以使用 OAuth 完成此操作,最值得注意的是客户端和服务器共享秘密的“2-legged OAuth”。见https://www.rfc-editor.org/rfc/rfc5849#page-14。在您的情况下,您希望省略oauth_token参数并可能使用 HMAC-SHA1 签名方法。这没有什么特别好说的。您无需通过 OAuth 令牌获取流程来执行此操作。这具有能够使用多个现有开源 OAuth 库中的任何一个的优势。

至于服务器端状态,您确实需要跟踪哪些秘密与哪些客户端相关,以及最近使用了哪些随机数(以防止重放攻击)。如果您通过 HTTPS 运行,您可以跳过随机数检查/生命周期,但如果您要这样做,那么 HTTPS + Basic Auth 可以为您提供您所描述的一切,而无需编写新软件。

于 2010-12-26T13:16:48.080 回答