没有会话的HTTP请求签名

时间:2010-12-22 20:18:10

标签: web-services http rest restful-authentication stateless-session

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

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

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

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

在考虑了它并与一些非常好的人交谈后,似乎没有任何休息服务可以做到这一点,就像它应该是我的用例一样简单。 (HTTP摘要和OAuth可以使用服务器状态执行此操作并且非常繁琐)

所以我想象一个,我问你的好友如何设计它(我会发布它的OpenSource,希望它能帮助别人)。

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

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 REQUEST将是:

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

OR

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签名=&lt;方法&GT; - &LT; USERID&GT; - &LT; SIGNATURE&GT; :将凭据放在URI中,而不是HTTP标头中。这允许用户共享签名请求;
  • _sras.date = Sun,1994年11月6日08:49:37 GMT(请求日期*):创建请求的日期。
  • _sras.expires = Sun,1994年11月6日08:49:37 GMT(到期日期*):告诉服务器请求不应在指定日期之前到期

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

感谢您的评论。

2 个答案:

答案 0 :(得分: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,否则,攻击者可能会为所有尚未达到重播消息的用户重播相同的消息。

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

答案 1 :(得分:1)

我会注意到你可以使用OAuth完成此任务,最明显的是“双腿OAuth”,其中客户端和服务器共享一个秘密。见http://tools.ietf.org/html/rfc5849#page-14。在您的情况下,您要省略oauth_token参数,并可能使用HMAC-SHA1签名方法。这没什么特别好吃的;您不需要通过OAuth令牌获取流程来执行此操作。这样做的好处是可以使用任何现有的开源OAuth库。

就服务器端状态而言,您需要跟踪哪些客户端的密码以及最近使用过哪些nonce(以防止重放攻击)。如果您通过HTTPS运行,可以跳过现时检查/生命周期,但如果您要这样做,那么HTTPS + Basic Auth会为您提供您描述的所有内容,而无需编写新软件。