JWK 什么时候过期? JWKS 轮换政策

When does JWK expire? JWKS rotation policy

我正在阅读有关 JWKS 的信息并找到了有关密钥轮换概念的信息 - https://developer.okta.com/docs/concepts/key-rotation/

假设我在我的应用程序中使用 JWKS,但我不定期获取它们,所以只是硬编码。 单键 JSON 对象看起来像

{
      "kty": "RSA",
      "e": "xxx",
      "use": "sig",
      "kid": "xxx",
      "x5t": "xx",
      "x5c": [
        "xxx"
      ],
      "n": "xxx

}

JWKS 为您提供 public 密钥,以便您验证 JWT。 现在提问。

  1. JWKS 到期后是否可以获取信息?例如,我可以使用 JWK 生成一个 .cert 文件并打开它来检查到期日吗?
  2. JWKS 提供程序是否在计划密钥轮换时公开信息或者可能是敏感信息?

请考虑上面的例子,所以我在应用程序中有密钥,想知道什么时候应该更换它们。

当然我知道这是不好的做法(我应该直接从 JWKS 端点获取密钥并且感觉安全)但这只是一个例子(如果这是一个愚蠢的例子,请提出一个更好的例子来描述上下文)。

  1. JSON Web 密钥集(JWKS 又名 JWK 集)是 JSON Web 密钥 (JWK) 的列表。由于 JWK Set 只是一个容器,因此它不包含过期等元数据 date/time.

  2. 至少出于两个原因,它没有暴露这一点:

  • RFC 7517 是管理 JWK 和 JWK Set 行为的规范。它没有提及或要求提供者发布到期时间 date/time。也许这是由于原因 #2:
  • 提供商应该能够随时出于任何原因删除密钥。可能的原因:密钥已被泄露。 (对于 private/public 密钥对,这意味着私钥已被泄露,并且应从流通中删除通过 JWKS 发布的相应 public 密钥)。这个例子是一个异常值,但它确实发生了,提供者必须立即采取行动来修复它。

尽管有紧急情况,但出于良好的安全卫生考虑,提供商会定期轮换密钥。要处理密钥轮换(无论是计划内还是紧急情况),您的应用程序应该遵循一个简单的算法。它应该定期从 JWKS 端点获取密钥,构建所有密钥的本地副本,并根据上次获取从该副本构建 add/remove 密钥。您的应用程序只能使用在本地副本中找到的密钥来执行加密操作,例如验证 JWT 上的签名。

每个JWK都有一个kid(key id)参数,这个参数用来匹配一个特定的key。 RFC 7517 建议在密钥翻转期间使用 kid 在 JWK 集中的一组密钥中进行选择。当您的应用程序从 JWKS 获取密钥时,您会将来自 JWK 的密钥集与本地副本中的密钥集进行比较。比较基于kid。如果带有一些 kid 的密钥存在于 JWKS 中但不存在于您的本地副本中,您应该将此密钥添加到您的副本中。反之亦然,如果具有一些 kid 的密钥存在于您的本地副本中但 不存在于 JWKS 中,您应该从您的本地副本中删除该密钥。

您的应用程序应多久从 JWKS 获取密钥一次?这取决于您,这取决于您的应用 and/or 您的组织的风险承受能力。有些应用程序每分钟提取一次,其他应用程序每小时或每天提取一次。

假设您的应用从不执行此提取操作,密钥已硬编码在您的应用中。这将一直有效,直到提供商删除密钥。 (我们假设我们在这里谈论的是 public 密钥。JWK 可以代表私钥......并且您 想要嵌入到您的应用程序)。一些供应商不会轮换密钥或在很长一段时间内轮换一次。如果您正在与一个(对您来说)知名的提供商打交道,并且他们向您保证不会轮换密钥,那么您将密钥嵌入到您的应用程序中的风险就很低。

一般来说,将 public 密钥嵌入到应用程序中并不是一个好主意。如果您要使用 JWKS 端点,请实施上面概述的简单获取 + 更新解决方案。

为实用起见,您应该根据密钥对安全性和用户体验的影响轮换密钥。这也取决于您的实施。

演员是

  • 客户端
  • 资源提供者 (RP)
  • 身份提供者 (IP) 实现细节...没有理由 IP 和 RP 不能是同一个实体,在所有这些 OIDC 东西之前就是这样。但如果您想扩展或使用备用 OIDC 提供商,以这些术语考虑它仍然是件好事)

Let's assume your implementation is that your access tokens are Json Web Signature (JWS) content of your JWT claims. The JWT in access token approach primarily replaces the notion of JSESSIONID along with the storage of such information in the backend and forwarding that storage responsibility to the client.

From the client perspective:

  1. If the client has no OAuth token it does some process (e.g. OIDC) to get the OAuth token

  2. If the client has an OAuth token it sends the access_token as the bearer to the RP

  3. If the RP responds with 401 or the client thinks the RP will respond with a 401.

    • the client will use the refresh token endpoint to get a new token from the IP
    • if the IP responds in error, the refresh process failed and the user is logged out
    • else continue
  4. Else just standard RP response

The 3rd step specifies the refresh token process. This needs to be noted as that also needs to be factored in when determining the rotation policy.

鉴于引用部分中的场景,只有一方真正需要关心签名,即为了识别用户和授权级别数据而使用 JWT 的一方,即 RP

bearer token 中的 JWT 会关联到特定的 JWS,JWT 可以重复使用,直到时间到期。由于这种情况经常发生,因此 RP 需要有一些层来快速执行此验证。它通过拥有由 IP 提供的签名者的 public 密钥来实现。在 OpenID Connect (OIDC) 标准中,它由 OpenID Discoveryjwks_uri 引用。无论 RP 如何检索它,RP 必须有一种可信的方式从 IP.

获取密钥

现在让我们输入一些变量

  • exp 访问令牌过期 时间不是持续时间
  • jwke JSON Web 密钥过期 时间不是持续时间
  • njwk 活跃的 JWK 数量
  • maxjwke JSON Web 密钥到期 DURATION
  • maxexp 访问网页密钥过期 DURATION

鉴于 jwke >= exp 否则客户端将意外收到 401,因为该密钥对其访问令牌不再有效。

现在为简单起见,您可以为每个访问令牌设置一个 JWK,这样可以工作并且相对安全,但会杀死您的后端,因为它一直在创建加密令牌。但这是最低限度。

所以至少我会有一组合理的 JWK 作为池,可以是 10 个也可以是 100 个 取决于你的容忍度

现在最大值是多少?那么 取决于您的容忍度 和创建代币的成本。

但是无论您的公差值如何,您需要确保的一件事是对于给定的 JWK,它应该 NOT 在访问时间之前过期。

IP 实现说明

现在假设您在 REDIS 中实施,它有两个主要限制:

  1. You cannot SCAN across cluster nodes
  2. Inability to expire hash elements

你可以按如下方式实现

  • 创建包含 public JWK 和私钥(存储为编码字节)的 njwk 元素的散列对
  • 每个哈希对都将根据某个时间块(例如星期几)进行加密,假设 maxexp 不会超过 maxjwke
  • 每个哈希对的默认到期时间为 maxjwke
  • 当密钥用于访问令牌时,它只会延长 PUBLIC 密钥的到期时间,使其等于 maxjwke + 剩余的访问令牌到期时间。 (此位确保访问令牌将在其具有的访问期限内工作。私钥仍设置为过期。
  • 在提供 JWKS 列表时,它应该提供当前和最后一组,只要它还没有过期。
  • 在选择密钥进行签名时,应从尚未过期的当前存储桶中选择。