REST Web 服务身份验证令牌实现

Posted

技术标签:

【中文标题】REST Web 服务身份验证令牌实现【英文标题】:REST Web Service authentication token implementation 【发布时间】:2013-03-07 13:59:19 【问题描述】:

我正在使用 C# 实现一个 REST Web 服务,它将作为云服务托管在 Azure 上。由于它是 REST 服务,因此它是无状态的,因此没有 cookie 或会话状态。

Web 服务只能通过 HTTPS 访问(证书由 StartSSL.com 提供)。

用户成功登录服务后,他们将获得一个安全令牌。此令牌将在未来的通信中提供身份验证。

令牌将包含客户端的时间戳、用户 ID 和 IP 地址。

所有通信都只能通过 HTTPS 进行,所以我不担心令牌被拦截并用于重放攻击;无论如何,令牌都会过期。

由于这是一项面向公众的服务,但我担心有人可能会注册该服务、登录然后修改他们收到的令牌以访问其他用户的帐户。

我想知道如何最好地保护令牌的内容并验证它没有被篡改。

我计划执行以下操作来保护令牌:

客户端成功登录服务,服务成功:

    生成一个随机值并使用 SHA256 对其进行 1000 次散列。 从私钥 + 散列随机值生成一次性会话密钥。 使用 SHA256 将会话密钥哈希 1000 次,然后使用它来加密令牌 使用私钥使用 RSA 对加密令牌进行签名。 将加密的令牌 + 签名 + 散列后的随机值以未加密的 JSON 包的形式发送给客户端。

当客户端调用服务时,它会将未加密 JSON 包中的加密令牌和签名发送到服务。该服务将

    从私钥 + 散列随机值重新创建会话密钥 使用私钥验证签名 使用哈希会话密钥解密令牌 检查令牌是否过期 继续请求的操作...

我对加密一无所知,所以我有一些问题:

    这样就够了还是有点矫枉过正? 我读到要检测篡改,我应该在令牌中包含一个 HMAC。由于我使用私钥签名,我还需要 HMAC 吗? 我应该使用 Rijndael 而不是 RSA? 如果首选 Rijndael,是否需要生成的 IV 才能解密?即我可以把它扔掉还是需要将它发送到加密令牌?例如加密令牌 + HMAC + IV + 散列随机值。

由于所有通信都是通过 HTTPS 进行的,因此未加密的 JSON 包在到达客户端之前并不是真正未加密的。

另外我可能想稍后在 php 中重新实现该服务,所以这一切都需要在 PHP 中实现。

感谢您的帮助

【问题讨论】:

【参考方案1】:

你真的想太多了。说实话,最好的代币安全性依赖于随机性,或者更准确地说 不可预测性。最好的令牌是完全随机的。你是对的,担心用户会修改他/她的令牌并使用它来访问其他人的帐户。这是一种称为“会话窃取”的常见攻击。当令牌在服务器端随机生成并过期时,这种攻击几乎是不可能的。使用 IP 和/或时间戳等用户信息是不好的做法,因为它提高了可预测性。我在大学进行了一次攻击,成功地猜出了基于服务器时间戳的活动令牌(以微秒为单位)。该应用程序的作者认为微秒的变化速度足够快,以至于无法预测,但事实并非如此。

您应该知道,当用户在代理服务器后面时,代理有时会以纯文本形式查看他们的 SSL 请求(出于安全原因,许多代理会执行深度数据包检查)。出于这个原因,最好让会话过期。如果您不这样做,您的用户将很容易受到此类攻击,也可能会受到 XSS 和 CSRF 的攻击。

只要提供合理的密钥长度,RSA 或 Rijndael 就足够了。此外,您应该使用带有令牌的 HMAC 来防止篡改,即使您正在对其进行签名。从理论上讲,这将是多余的,因为您正在使用私钥签名。但是,HMAC 已经过很好的测试,您对签名机制的实现可能存在缺陷。因此,最好使用 HMAC。您会惊讶于有多少“自行开发”的安全实现存在导致它们妥协的缺陷。

你听起来很精通安全。保持良好的工作!在这个世界上,我们需要更多有安全意识的开发人员。

编辑:

在令牌中包含时间戳/用户 ID 被认为是安全的,只要它们使用只有服务器拥有的强 对称 密钥(如 AES、Blowfish 等)进行加密,并且作为只要令牌包含一个防篡改哈希,例如 HMAC,它使用密钥和用户 ID/时间戳进行加密。散列保证完整性,加密保证机密性。

如果您没有在加密中包含 HMAC(或其他哈希),那么用户就有可能篡改加密的令牌并将其解密为有效的东西。我对服务器进行了攻击,其中用户 ID 和时间戳被加密并用作没有哈希的令牌。通过更改字符串中的一个随机字符,我能够将我的用户 ID 从 58762 更改为 58531。虽然我无法选择“新”用户 ID,但我能够访问其他人的帐户(这是在学术界,作为课程的一部分)。

另一种方法是使用完全随机的令牌值,并将其在服务器端映射到存储的用户 ID/时间戳(保留在服务器端,因此不受客户端控制)。这需要更多的内存和处理能力,但更安全。这是您必须根据具体情况做出的决定。

对于从 IV 和其他密钥重用/派生密钥,这通常是可以的,前提是这些密钥仅在短时间内有效。从数学上讲,有人不太可能打破它们。然而这是可能的。如果您想走偏执路线(我通常这样做),请随机生成所有新密钥。

【讨论】:

谢谢。我已决定使用 Rijndael 和 HMAC,但仍在努力解决它。我可以使用 IV 从主密钥中派生会话密钥吗?我是否也可以为 HMAC 使用相同的会话密钥,还是需要从当前会话密钥派生一个新密钥?如果我需要一个新的 HMAC 密钥,我可以使用当前的 IV 还是我也需要一个新的随机值?最后你提到我不应该用用户 ID 加密时间戳,那我怎么知道令牌已经过期?因为我无法在数据库中存储会话记录。再次感谢。 好问题。我会用一些额外的信息更新答案 好的,我明白了。那么HMAC是从未加密的数据中计算出来的,然后用数据加密呢?我是否需要将 HMAC 存储在服务器上以便稍后进行比较,或者我可以解密令牌,然后从现在未加密的数据中计算 HMAC,并将其与使用数据加密的 HMAC 进行比较? HMAC 可以从未加密或加密数据中计算出来,只要 HMAC 在用户控制下使用密钥加密即可。永远不要让用户访问未加密的 HMAC,因为他们可以篡改数据(即使它是加密的)并相应地更新 HMAC。您不需要将HMAC存储在服务器上,只要您存储密钥并根据未加密的数据重新计算HMAC,并与客户端提交的现在未加密的HMAC进行验证即可。这有意义吗? 是的,这是有道理的,您的答案清晰易懂。我想我可能会使用随机数令牌并维护一个会话表,而不是传递用户 ID、时间戳等。这违背了宁静的设计,但它为更好的安全性付出了很小的代价。

以上是关于REST Web 服务身份验证令牌实现的主要内容,如果未能解决你的问题,请参考以下文章

Rest Web 服务:具有令牌安全性的匿名和经过身份验证的用户

Django REST 的 JSON Web 令牌不会向用户数据库进行身份验证

会话管理:如何为 REST 服务生成身份验证令牌? (球衣)

如何使 Django REST JWT 身份验证与多个 Web 服务器一起扩展?

JWT身份验证及其替代方案,可实现Web应用程序后端的RESTfulness

用于 Azure 移动服务 (REST) 的 Live Connect 身份验证令牌