如何在服务器上安全地存储不记名令牌?

信息安全 oauth 贮存 令牌 联邦
2021-09-11 11:16:34

想象一下,您正在运行一个实现 OAuth 2.0 流程的服务,以允许您的最终用户登录到 3rd 方应用程序,并授权这些应用程序通过某些 API 使用您的服务数据。
通过您的 OAuth 流成功验证第 3 方应用程序后,您的服务会向第 3 方应用程序发送一个不记名令牌,用于验证(并授权)第 3 方应用程序(代表最终用户)使用您的服务的 API。

这样的不记名令牌需要存储在您的服务器上,以便对传入的 API 调用执行身份验证。由于不记名令牌对客户端进行身份验证(和授权),因此您需要安全地存储它,就像存储密码一样。
然而,虽然密码在保存到磁盘之前会被加盐 + 散列,但它们也会通过相应的主体(用户名/电子邮件地址)而不是通过密码的安全表示来查找。

在不记名令牌的情况下,没有用于查找匹配凭证/令牌的主体。假设您要存储不记名令牌,如密码(加盐+散列),您必须查找所有可能的盐,应用每个盐,然后散列加盐令牌,然后检查它是否存在于系统中的任何位置。那不成比例。

所以我的问题是:如何在服务器上安全地存储不记名令牌?也就是说,无需在需要验证令牌时生成所有可能的安全表示。
“安全存储不记名令牌”是指获得所有“安全存储”不记名令牌文件的攻击者将无法使用它们。

2个回答

如果第三方向 OAuth 提供者发出的请求包含一些将请求与用户相关联的其他数据,例如用户的姓名或 ID,则服务器可以将不记名令牌存储为带有单个盐的常规密码。但是,如果第三方请求没有可识别信息,则服务器可以存储不记名令牌的加盐+散列版本,并为所有不记名令牌共享盐。因为 OAuth 令牌很快过期,所以与可能永不过期的密码相比,唯一的盐几乎没有那么重要。如果令牌在 6-12 小时后过期,攻击者就无法真正破解哈希并使用它来访问用户帐户。然后服务器可以每周左右定期轮换盐以增加安全性。

根据贾斯汀的回答,我正在构建自己的。

如果标记足够长(在我的情况下,它们是 200 个字母数字 ( [A-Za-z0-9]) 字符,则无需对它们加盐。单独散列就足够了,因为复杂性足够高。

如果令牌不够长而无法仅使用散列(或者如果您想坚持对它们进行加盐),您可以稍微修改令牌生成方案:生成一个特定于令牌的主体,而不是生成一个随机令牌。然后将两者连接起来,并将结果作为单个令牌交给消费者应用程序。这本质上是要求 API 客户端也发送用户识别信息,但它方便地嵌入到单个令牌中。

在内部,您在存储之前对实际令牌进行加盐 + 哈希处理,并单独存储特定于令牌的主体。在验证令牌时,您将提交的令牌一分为二,取回实际令牌和特定于令牌的主体。然后可以使用此主体查找可以运行匹配器的加盐+散列的实际令牌。

具体例子:

  1. 生成不记名令牌:例如 a0z9B1Y8c2x7
  2. 生成一个固定长度的特定于令牌的主体:例如 D3W6e4v5F5
  3. 生成盐,例如 aabbccddeeff00112233445566778899
  4. Salt+hash 不记名令牌 ( a0z9B1Y8c2x7),这将导致如下所示:xyzXYZabcABC
  5. 将 salted+hashed 令牌与特定于令牌的主体一起存储,即存储一个元组 (xyzXYZabcABC, D3W6e4v5F5)
  6. 将令牌和主体的连接提供给用户,以将其视为常规不记名令牌: a0z9B1Y8c2x7D3W6e4v5F5