| RFC 8725 | JWT BCP | 2020年2月 |
| Sheffer 等 | 最佳当前实践 | [Page] |
JSON Web Token,也称为 JWT,是基于 JSON 的 URL 安全安全令牌, 它包含一组可以被签名和/或加密的声明。JWT 正被广泛使用, 作为众多协议和应用中简单的安全令牌格式,无论是在数字身份 领域还是其他应用领域。本最佳当前实践文档更新了 RFC 7519, 提供可操作的指导,以实现 JWT 的安全实现和部署。¶
本备忘录记录了互联网的最佳当前实践。¶
本文档是互联网工程任务组(IETF)的产物。它代表了 IETF 社区的共识。 它已接受公众审查,并已获互联网工程指导组(IESG)批准发布。 关于 BCP 的更多信息,请参见 RFC 7841 的第 2 节。¶
有关本文档的当前状态、任何勘误信息以及如何提供反馈,可在以下网址获取: https://www.rfc-editor.org/info/rfc8725。¶
Copyright (c) 2020 IETF Trust and the persons identified as the document authors. All rights reserved.¶
This document is subject to BCP 78 and the IETF Trust's Legal Provisions Relating to IETF Documents (https://trustee.ietf.org/license-info) in effect on the date of publication of this document. Please review these documents carefully, as they describe your rights and restrictions with respect to this document. Code Components extracted from this document must include Simplified BSD License text as described in Section 4.e of the Trust Legal Provisions and are provided without warranty as described in the Simplified BSD License.¶
JSON Web Token,也称为 JWT [RFC7519],是一种 URL 安全的基于 JSON 的安全令牌, 包含一组可被签名和/或加密的声明。JWT 规范得到了快速推广,因为它将与安全相关的信息 封装在一个易于保护的位置,同时使用广泛可用的工具实现起来也很简单。 JWT 常见的应用场景之一是表示数字身份信息,例如 OpenID Connect ID Token [OpenID.Core], 以及 OAuth 2.0 [RFC6749] 的访问令牌 和刷新令牌,其具体细节依赖于部署环境。¶
自 JWT 规范发布以来,已经有多起广泛报道的针对实现和部署的攻击事件。 此类攻击通常源于安全机制定义不充分、实现不完整或应用程序使用不当。¶
本文件的目标是促进 JWT 的安全实现和部署。 文档中的许多建议涉及 JWT 所基于的加密机制的实现和使用,这些机制由 JSON Web Signature (JWS) [RFC7515]、 JSON Web Encryption (JWE) [RFC7516] 和 JSON Web Algorithms (JWA) [RFC7518] 定义。 其他建议则涉及 JWT 声明的使用。¶
这些建议旨在作为在绝大多数实现和部署场景中使用 JWT 的最低要求。 引用本文件的其他规范可能会基于其特定情况对格式的某些方面提出更严格的要求; 在这种情况下,建议实现者遵循更严格的要求。此外,本文件提供的是下限,而非上限, 因此总是允许采用更强的选项(例如,根据对加密强度与计算负载重要性的不同评估)。¶
社区关于各种算法强度和可行攻击的认知可能会迅速变化,经验表明, 安全最佳实践(BCP)文档只是一个时间点的陈述。建议读者查阅适用于本文件的勘误或更新。¶
本节列出了一些已知和可能存在的 JWT 实现和部署问题。 每个问题描述后都附有针对这些问题的一个或多个缓解措施的参考。¶
已签名的 JSON Web Token 会携带签名算法的明确指示, 以 "alg" Header 参数的形式,便于加密灵活性。 这与某些库和应用中的设计缺陷相结合, 导致了若干攻击:¶
此外,一些应用使用密钥消息认证码(MAC)算法(例如 "HS256")对令牌进行签名,但提供的对称密钥过弱,熵不足(例如易记密码)。 一旦攻击者获取此类令牌,这类密钥容易受到离线暴力破解或字典攻击 [Langkemper]。¶
许多加密算法会泄露明文长度信息,泄露量因算法和操作模式而异。 当明文最初被压缩时,此问题会加剧,因为压缩后的明文长度及密文长度不仅取决于原始明文长度,还取决于其内容。 当攻击者控制的数据与秘密数据位于同一压缩空间时,压缩攻击尤其强大,这在对 HTTPS 的某些攻击中是可能的。¶
关于压缩和加密的一般背景,请参见 [Kelsey]; 关于 HTTP Cookie 攻击的具体示例,请参见 [Alawatugoda]。¶
根据 [Sanso],若干 Javascript 对象签名与加密(JOSE)库在执行椭圆曲线密钥协商("ECDH-ES" 算法)时, 未正确验证其输入。 攻击者如果能够发送其选择的、使用无效曲线点的 JWE,并观察解密无效曲线点所得到的明文输出, 可以利用此漏洞恢复接收方的私钥。¶
早期 JSON 格式版本,如已废弃的 [RFC7159], 允许多种字符编码:UTF-8、UTF-16 和 UTF-32。最新标准 [RFC8259] 除闭合环境内部使用外,仅允许 UTF-8。 这种歧义,即老版本实现或闭合环境内的实现可能生成非标准编码, 可能导致 JWT 被接收方误解,从而被恶意发送方利用以绕过验证检查。¶
存在一种攻击,其中某接收方会收到原本针对其的 JWT, 并试图在不适用该 JWT 的其他接收方处使用它。 例如,如果 OAuth 2.0 [RFC6749] 访问令牌被合法地用于其目标受保护资源,该资源可能将相同访问令牌提供给其他不适用该令牌的资源以尝试访问。 若此类情况未被发现,可能导致攻击者访问其无权访问的资源。¶
以下列出的最佳实践应由执行者应用,以减轻前一节中列出的威胁。¶
库 MUST 允许调用者指定一个支持的算法集并且 MUST NOT 在执行加密操作时使用任何其他算法。 库 MUST 必须确保 "alg" 或 "enc" 头指定与加密操作使用的算法相同。 此外,每个密钥 MUST 仅用于恰好一种算法,并且在执行加密操作时 必须检查这一点。¶
如 [RFC7515] 第5.2节 所述, “在给定的上下文中可以使用哪些算法是应用程序的决定。即使一个 JWS 可以被成功验证, 除非 JWS 中使用的算法为应用程序所接受,否则应用程序 SHOULD 应将该 JWS 视为无效。”¶
因此,应用程序 MUST 仅允许使用满足应用程序安全 要求的当前加密算法。 随着新算法的引入以及现有算法因发现加密弱点而被弃用,这个集合会随时间变化。 因此,应用程序 MUST 应设计为支持密码学灵活性(cryptographic agility)。¶
也就是说,如果一个 JWT 通过传输层(例如使用当前加密算法的 TLS) 得到端到端的加密保护,可能无需对 JWT 再施加另一层加密保护。 在这种情况下,使用 "none" 算法可能是完全可接受的。 仅当 JWT 通过其他方式得到加密保护时才应使用 "none" 算法。 在某些应用场景中,使用 "none" 的 JWT 常被用作内容可选签名;此时,带签名和不带签名情况下 URL 安全的声明表示和处理可以保持一致。 JWT 库 SHOULD NOT 生成使用 "none" 的 JWT,除非调用者明确请求这样做。 同样,JWT 库 SHOULD NOT 消费使用 "none" 的 JWT,除非调用者明确请求这样做。¶
应用程序 SHOULD 遵循以下与算法相关的建议:¶
JWT 中使用的所有加密操作 MUST 得到验证, 如果其中任何一个验证失败,则整个 JWT MUST 被拒绝。 这不仅适用于仅具有一组头参数的 JWT,也适用于嵌套的 JWT,其中外部和内部的操作都 MUST 使用由应用程序提供的密钥和算法进行验证。¶
某些加密操作,例如椭圆曲线 Diffie-Hellman 密钥协商(“ECDH-ES”), 接受的输入可能包含无效值。这包括不在指定椭圆上的点或其他无效点(例如,[Valenta],第7.1节)。 JWS/JWE 库本身必须在使用这些输入之前验证它们,或者必须使用能够进行此类验证的底层加密库(或两者兼用)。¶
椭圆曲线 Diffie-Hellman 临时-静态(ECDH-ES)的临时公钥(epk)输入 应根据接收方选择的椭圆进行验证。对于 NIST 素数阶曲线 P-256、P-384 和 P-521, 验证 MUST 按照“Recommendation for Pair-Wise Key-Establishment Schemes Using Discrete Logarithm Cryptography” 的第5.6.2.3.4 节(ECC 部分公钥验证例程)执行([nist-sp-800-56a-r3])。 如果使用 "X25519" 或 "X448"([RFC8037])算法, 则应遵循 [RFC8037] 中的安全注意事项。¶
必须遵循 [RFC7515] 第10.1节 中关于密钥熵和随机值的建议,以及 [RFC7518] 第8.8节 中的密码考虑。 特别地,人类可记忆的密码 MUST NOT 直接用作诸如 "HS256" 之类的 keyed-MAC 算法的密钥。此外,密码应仅用于执行密钥加密,而不是用于内容加密, 如 [RFC7518] 第4.8节 所述。 请注意,即使用于密钥加密,基于密码的加密仍然容易受到暴力破解攻击。¶
在加密之前不应对数据进行压缩(SHOULD NOT), 因为此类压缩数据往往会泄露关于明文的信息。¶
[RFC7515], [RFC7516], 和 [RFC7519] 都规定应使用 UTF-8 对用于头参数和 JWT 声明集的 JSON 进行编码和解码。这也符合最新的 JSON 规范 [RFC8259]。 实现和应用程序 MUST 应当这样做,并且不得使用或接受其他 Unicode 编码用于这些目的。¶
当 JWT 包含 "iss"(发行者)声明时,应用程序 MUST 验证用于 JWT 中加密操作的加密密钥确实属于该发行者。 如果不属于,应用程序 MUST 拒绝该 JWT。¶
确定发行者拥有的密钥的方式取决于应用程序。例如,OpenID Connect [OpenID.Core] 的发行者值是指向 JSON 元数据文档的 "https" URL, 该文档包含一个 "jwks_uri" 值,即一个 "https" URL,用于检索该发行者的密钥作为一个 JWK 集([RFC7517])。 相同的机制也被 [RFC8414] 使用。 其他应用程序可能使用不同的方式将密钥绑定到发行者。¶
类似地,当 JWT 包含 "sub"(主题)声明时,应用程序 MUST 验证该主题值在应用程序中是否对应于一个有效的主题和/或有效的发行者-主题对。 这可能包括确认发行者是否被应用程序信任。 如果发行者、主题或该对无效,应用程序 MUST 拒绝该 JWT。¶
如果同一发行者可以签发针对多个依赖方或应用程序使用的 JWT, 则该 JWT MUST 必须包含一个 "aud"(受众)声明,用于确定该 JWT 是否被预期方使用,或是否被攻击者替换为在非预期方使用。¶
在这种情况下,依赖方或应用程序 MUST 应验证受众值,如果受众值不存在或与接收方不相关联, 则 MUST 拒绝该 JWT。¶
"kid"(密钥 ID)头由依赖应用程序用于执行密钥查找。应用程序 应确保这不会通过验证和/或清理接收到的值而导致 SQL 或 LDAP 注入漏洞。¶
类似地,盲目遵循可能包含任意 URL 的 "jku"(JWK 集 URL)或 "x5u"(X.509 URL)头 可能导致服务器端请求伪造(SSRF)攻击。应用程序 SHOULD 采取防护措施, 例如将 URL 与允许位置的白名单匹配,并确保在 GET 请求中不发送任何 cookie。¶
有时,一种类型的 JWT 可能会被误认为是另一种。如果某种特定的 JWT 易受此类混淆, 该 JWT 可以包含一个显式的 JWT 类型值,并且验证规则可以指定检查该类型。 此机制可以防止此类混淆。 使用 "typ" 头参数可以实现显式的 JWT 类型标注。 例如,[RFC8417] 规范使用 "application/secevent+jwt" 媒体类型 来对安全事件令牌(SETs)进行显式类型标注。¶
根据 [RFC7515] 第4.1.9节 对 "typ" 的定义,建议在 "typ" 值中省略 "application/" 前缀(RECOMMENDED)。 因此,例如,用于显式标注 SET 类型的 "typ" 值 SHOULD 为 "secevent+jwt"。 当对 JWT 使用显式类型标注时,建议使用格式为 "application/example+jwt" 的媒体类型名称,其中 "example" 被替换为特定 JWT 类型的标识符。¶
在对嵌套 JWT 应用显式类型时,包含显式类型值的 "typ" 头参数 MUST 出现在嵌套 JWT 的内部 JWT 中(即其有效负载为 JWT 声明集的 JWT)。 在某些情况下,相同的 "typ" 头参数值也会出现在外部 JWT 中,以显式标注整个嵌套 JWT。¶
注意,显式类型的使用可能无法与现有类型的 JWT 实现完全去歧义, 因为现有类型的 JWT 的验证规则通常并不使用 "typ" 头参数值。 对于新的 JWT 用途,建议使用显式类型(RECOMMENDED)。¶
每个 JWT 的应用都定义了一个概要文件,规定了必需和可选的 JWT 声明 以及与之相关的验证规则。 如果同一发行者可以签发多种类型的 JWT, 则这些 JWT 的验证规则 MUST 必须被编写为互斥的, 拒绝属于错误类型的 JWT。 为防止将一种上下文中的 JWT 替换到另一种上下文中,应用开发者可以采用多种策略:¶
鉴于 JWT 使用和应用的广泛多样性,用于区分不同类型 JWT 的类型组合、必需声明、值、头参数、密钥用法和发行者 在一般情况下将是应用程序特定的。如在 第3.11节 所讨论的,对于新的 JWT 应用, 建议使用显式类型(RECOMMENDED)。¶
感谢 Antonio Sanso 将 “ECDH-ES” 无效点攻击 提示给 JWE 和 JWT 实现者。 Tim McLean 发表了 RSA/HMAC 混淆攻击 [McLean]。 感谢 Nat Sakimura 提倡使用显式类型。感谢 Neil Madden 的大量评论,并感谢 Carsten Bormann, Brian Campbell, Brian Carpenter, Alissa Cooper, Roman Danyliw, Ben Kaduk, Mirja Kühlewind, Barry Leiba, Eric Rescorla, Adam Roach, Martin Vigoureux, and Éric Vyncke 的审阅。¶