没有会话的 HTTP 请求签名

发布于 2024-10-09 00:06:47 字数 2087 浏览 0 评论 0原文

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

  • 该请求是由声明该请求的用户生成的;
  • 请求没有被其他人修改过(uri/method/content/date);
  • 对于 GET 请求,应该可以生成一个包含足够信息的 URI,以检查签名并设置到期日期。这样,用户可以在有限的时间内将具有生成的 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= <方法>-<用户ID>-<签名> :将凭据放入 URI 中,而不是 HTTP 标头中。这允许用户共享签名的请求;
  • _sras.date=Sun, 06 Nov 1994 08:49:37 GMT(请求日期*):创建请求的日期。
  • _sras.expires=Sun, 06 Nov 1994 08:49:37 GMT(过期日期*):告诉服务器请求不应在指定日期之前过期

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

谢谢征求您的意见。

I am thinking of a rest web service that ensure for every request sent to him that :

  • The request was generated by the user who claim it ;
  • The request has not been modified by someone else (uri/method/content/date);
  • For GET requests, it should be possible to generate a URI with enough information in it to check the signature and set a date of expiration. That way a user can delegate temporary READ permissions to a collaborator for a limited time period on a ressource with a generated URI.

Clients are authenticated with id and a content-signature based on their password.

There should be no session at all, and so server state ! The server and the client share a secret key (a password)

After thinking about it and talking with some really nice folks, it seems there is no rest service existing to do that as simple as it should be for my use case. (HTTP Digest and OAuth can do this with server state and are very chatty)

So I Imagined one, and I'm asking your greats comments on how it should be designed (I will release it OpenSource and Hope it can help others).

The service use a custom "Content-signature" header to store credentials. An authenticated request should contains this header :

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

A request is invalidated 10 minutes after it has been created.

For example a typical HTTP REQUEST would be :

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" }

The server will answer :

401 Unauthorized

OR

200 OK

Variables would be :

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

URI Parameters

Some parameters can be added to the URI (they overload the headers informations) :

  • _sras.content-signature=<METHOD>-<USERID>-<SIGNATURE> : PUT the credentials in the URI, not in the HTTP header. This allow a user to share a signed request ;
  • _sras.date=Sun, 06 Nov 1994 08:49:37 GMT (request date*) : The date when the request was created.
  • _sras.expires=Sun, 06 Nov 1994 08:49:37 GMT (expire date*) : Tell the server the request should not expire before the specified date

*date format : http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.18

Thanks for your comments.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

冷默言语 2024-10-16 00:06:47

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

1- 通常会在非标准标头中添加“X-Namespace-”前缀,在您的情况下,您可以将标头命名为:“X-SRAS-Content -签名”。

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

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

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

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

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

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

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

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

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

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

There are several issues that you need to consider when designing a signature protocol. Some of these issues might not apply to your particular service:

1- It is customary to add an "X-Namespace-" prefix to non-standard headers, in your case you could name your header something like: "X-SRAS-Content-Signature".

2- The Date header might not provide enough resolution for the nonce value, I would therefore advise for a timestamp having at least 1 millisecond of resolution.

3- If you do not store at least the last nonce, one could still replay a message in the 10 minutes window, which is probably unacceptable on a POST request (could create multiple instances with same values in your REST web service). This should not be a problem for GET PUT or DELETE verbs.

However, on a PUT, this could be used for a denial of service attack by forcing to update many times the same object within the proposed 10 minutes window. On a GET or DELETE a similar problem exists.

You therefore probably need to store at least the last used nonce associated with each user id and share this state between all your authentication servers in real-time.

4- This method also requires that the client and servers be clock synchronized with less than 10 minutes skew. This can be tricky to debug, or impossible to enforce if you have AJAX clients for which you do not control the clock. This also requires to set all timestamps in UTC.

An alternative is to drop the 10 minutes window requirement but verify that timestamps increase monotonically, which again requires to store the last nonce. This is still a problem if the client's clock is updated to a date prior to the last used nonce. Access would be denied until the client's clock pass the last nonce or the server nonce state is reset.

A monotonically increasing counter is not an option for clients that cannot store a state, unless the client could request the last used nonce to the server. This would be done once at the beginning of each session and then the counter would be incremented at each request.

5- You also need to pay attention to retransmissions due to networks errors. You cannot assume that the server has not received the last message for which a TCP Ack has not been received by the client before the TCP connection dropped. Therefore the nonce needs to be incremented between each retransmission above the TCP level and the signature re-calculated with the new nonce. Yet a message number needs to be added to prevent double execution on the server: a double POST would result in 2 object being created.

6- You also need to sign the userid, otherwise, an attacker might be able to replay the same message for all users which nonces have not yet reached that of the replayed message.

7- Your method does not guaranty the client that the server is authentic and has not been DNS-hijacked. Server authentication is usually considered important for secure communications. This service could be provided by signing responses from the server, using the same nonce as that of the request.

情归归情 2024-10-16 00:06:47

我要指出的是,您可以使用 OAuth 来完成此任务,尤其是“两条腿的 OAuth”,其中客户端和服务器共享一个秘密。请参阅 https://www.rfc-editor.org/rfc/rfc5849#page -14。在您的情况下,您希望省略 oauth_token 参数,并可能使用 HMAC-SHA1 签名方法。这并没有什么特别闲聊的。您不需要通过 OAuth 令牌获取流程来执行此操作。这样做的优点是能够使用任何现有的开源 OAuth 库。

就服务器端状态而言,您确实需要跟踪哪些秘密与哪些客户端相关,以及最近使用了哪些随机数(以防止重放攻击)。如果您通过 HTTPS 运行,您可以跳过随机数检查/生命周期,但如果您打算这样做,那么 HTTPS + 基本身份验证将为您提供您所描述的所有内容,而无需编写新软件。

I would note that you can accomplish this with OAuth, most notably "2-legged OAuth" where client and server share a secret. See https://www.rfc-editor.org/rfc/rfc5849#page-14. In your case, you want to omit the oauth_token parameter and probably use the HMAC-SHA1 signature method. There's nothing particularly chatty about this; you don't need to go through the OAuth token acquisition flows to do things this way. This has the advantage of being able to use any of several existing open source OAuth libraries.

As far as server-side state, you do need to keep track of what secrets go with which clients, as well as which nonces have been used recently (to prevent replay attacks). You can skip the nonce checking / lifetimes if you run things over HTTPS, but if you're going to do that, then HTTPS + Basic Auth gives you everything you described without having to write new software.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文