RFC 8725

JSON Web Token Best Current Practices

πŸ“… published February 2020, from ietf

RFC 8725: JSON Web Token Best Current Practices πŸŽ€

okay so bestie, this is the title block πŸ’… it tells you who wrote it, what kind of doc it is, and what it touches:

  • Stream: Internet Engineering Task Force (IETF) 🌸
  • Request for Comments: 8725
  • BCP: 225
  • Category: Best Current Practice ✨
  • Authors: Y. Sheffer (Intuit), D. Hardt, M. Jones (Microsoft) πŸ‘‘
  • Updates: 7519 (that's [RFC7519], the JWT spec itself)
  • Obsoletes: nothing listed πŸ¦‹
  • ISSN: 2070-1721
  • Date: February 2020 πŸ“…

Abstract 🫢

okay so JSON Web Tokens, aka JWTs, are URL-safe, JSON-based security tokens that carry a set of claims, and those claims can be signed and/or encrypted πŸ”. JWTs are everywhere now as a simple security token format across loads of protocols and apps, both in digital identity and in other areas too 🌈. This Best Current Practices doc updates RFC 7519 so it gives you actionable guidance for building and deploying JWTs securely πŸ’–.

Status of This Memo πŸ“

This memo documents an Internet Best Current Practice πŸ’œ.

This document is a product of the Internet Engineering Task Force (IETF). It represents the consensus of the IETF community. It went through public review and got approved for publication by the Internet Engineering Steering Group (IESG) βœ…. More info on BCPs is in Section 2 of RFC 7841 🧚.

For the current status of this document, any errata, and how to send feedback, check 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 docs carefully, because they describe your rights and restrictions for 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 they're provided without warranty as described in the Simplified BSD License πŸ“.

Table of Contents πŸ—ΊοΈ

1.  Introduction
  1.1.  Target Audience
  1.2.  Conventions Used in this Document
2.  Threats and Vulnerabilities
  2.1.  Weak Signatures and Insufficient Signature Validation
  2.2.  Weak Symmetric Keys
  2.3.  Incorrect Composition of Encryption and Signature
  2.4.  Plaintext Leakage through Analysis of Ciphertext Length
  2.5.  Insecure Use of Elliptic Curve Encryption
  2.6.  Multiplicity of JSON Encodings
  2.7.  Substitution Attacks
  2.8.  Cross-JWT Confusion
  2.9.  Indirect Attacks on the Server
3.  Best Practices
  3.1.  Perform Algorithm Verification
  3.2.  Use Appropriate Algorithms
  3.3.  Validate All Cryptographic Operations
  3.4.  Validate Cryptographic Inputs
  3.5.  Ensure Cryptographic Keys Have Sufficient Entropy
  3.6.  Avoid Compression of Encryption Inputs
  3.7.  Use UTF-8
  3.8.  Validate Issuer and Subject
  3.9.  Use and Validate Audience
  3.10. Do Not Trust Received Claims
  3.11. Use Explicit Typing
  3.12. Use Mutually Exclusive Validation Rules for Different Kinds
        of JWTs
4.  Security Considerations
5.  IANA Considerations
6.  References
  6.1.  Normative References
  6.2.  Informative References
Acknowledgements
Authors' Addresses

1. Introduction 🌷

okay so JWTs, also known as JWTs [RFC7519], are URL-safe, JSON-based security tokens holding a set of claims that can be signed and/or encrypted πŸ’–. The JWT spec blew up fast because it packs security-relevant info into one easy-to-protect spot, and because it's simple to implement with tools everyone already has πŸ› οΈ. One big use is representing digital identity info, like OpenID Connect ID Tokens [OpenID.Core] and OAuth 2.0 [RFC6749] access tokens and refresh tokens. The details of those depend on the deployment πŸ¦‹.

Ever since the JWT spec came out, there have been several widely published attacks on implementations and deployments 😭. Those attacks happen because security mechanisms were under-specified, implementations were incomplete, and apps used things incorrectly πŸ’€.

The goal here is to make it easier to implement and deploy JWTs securely ✨. A lot of the recommendations are about how to implement and use the crypto mechanisms underneath JWTs, which are defined by JSON Web Signature (JWS) [RFC7515], JSON Web Encryption (JWE) [RFC7516], and JSON Web Algorithms (JWA) [RFC7518]. Others are about how to use the JWT claims themselves πŸŽ€.

These are meant to be minimum recommendations for using JWTs in the vast majority of implementation and deployment scenarios 🌈. Other specs that reference this doc can have stricter requirements about one or more parts of the format, based on their own circumstances. When that's the case, implementers are advised to follow those stricter requirements 🧚. Also, this doc is a floor, not a ceiling, so stronger options are always allowed. For example, you might weigh cryptographic strength against computational load differently πŸ’ͺ.

What the community knows about algorithm strength and feasible attacks can change really fast πŸƒβ€β™€οΈ. Experience shows that a security BCP is a point-in-time statement, so readers should go look for any errata or updates that apply to this doc πŸ”.

1.1. Target Audience 🎯

The intended audiences are:

  • Implementers of JWT libraries (and the JWS and JWE libraries those libraries use) πŸ“š,
  • Implementers of code that uses such libraries (to the extent that some mechanisms may not be provided by libraries, or until they are) πŸ’», and
  • Developers of specifications that rely on JWTs, both inside and outside the IETF 🫢.

1.2. Conventions Used in this Document πŸ”€

The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "NOT RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be interpreted as described in BCP 14 [RFC2119] [RFC8174] when, and only when, they appear in all capitals, as shown here πŸŽ€.

2. Threats and Vulnerabilities 😈

This section lists some known and possible problems with JWT implementations and deployments πŸ‘€. Each problem description is followed by references to one or more mitigations πŸ›‘οΈ.

2.1. Weak Signatures and Insufficient Signature Validation 🫠

Signed JWTs carry an explicit indication of the signing algorithm, in the form of the alg Header Parameter, to make crypto agility easier πŸ¦‹. Combined with design flaws in some libraries and apps, this has led to several attacks πŸ’€:

  • The algorithm can be changed to none by an attacker, and some libraries would trust that value and "validate" the JWT without checking any signature 😱.
  • An RS256 (RSA, 2048 bit) parameter value can be changed into HS256 (HMAC, SHA-256), and some libraries would then try to validate the signature using HMAC-SHA256 with the RSA public key as the HMAC shared secret (see [McLean] and [CVE-2015-9235]) πŸ™ˆ.

For mitigations, see Sections 3.1 and 3.2 🌸.

2.2. Weak Symmetric Keys πŸ”‘

Some apps use a keyed Message Authentication Code (MAC) algorithm, like HS256, to sign tokens, but they supply a weak symmetric key with insufficient entropy, such as a human-memorable password πŸ₯². Those keys are vulnerable to offline brute-force or dictionary attacks once an attacker gets hold of a token [Langkemper] πŸ’€.

For mitigations, see Section 3.5 ✨.

2.3. Incorrect Composition of Encryption and Signature 🧩

Some libraries that decrypt a JWE-encrypted JWT to get a JWS-signed object don't always validate the internal signature 😬.

For mitigations, see Section 3.3 πŸ’….

2.4. Plaintext Leakage through Analysis of Ciphertext Length πŸ“

Lots of encryption algorithms leak info about the length of the plaintext, and how much they leak depends on the algorithm and mode of operation 🫣. This gets worse when the plaintext is compressed first, because the length of the compressed plaintext (and so the ciphertext) depends not only on the length of the original plaintext but also on its content πŸŒ€. Compression attacks are especially powerful when attacker-controlled data sits in the same compression space as secret data, which is the case for some attacks on HTTPS πŸ”“.

See [Kelsey] for general background on compression and encryption, and [Alawatugoda] for a specific example of attacks on HTTP cookies πŸͺ.

For mitigations, see Section 3.6 πŸŽ€.

2.5. Insecure Use of Elliptic Curve Encryption πŸͺ„

Per [Sanso], several Javascript Object Signing and Encryption (JOSE) libraries fail to validate their inputs correctly when doing elliptic curve key agreement (the "ECDH-ES" algorithm) 😭. An attacker who can send JWEs of its choosing that use invalid curve points, and who can observe the cleartext outputs that come out of decryption with those invalid curve points, can use this vulnerability to recover the recipient's private key 😱.

For mitigations, see Section 3.4 🫢.

2.6. Multiplicity of JSON Encodings πŸ”€

Older versions of the JSON format, like the obsoleted [RFC7159], allowed several different character encodings: UTF-8, UTF-16, and UTF-32 🌍. That's not the case anymore, because the latest standard, [RFC8259], only allows UTF-8, except for internal use within a "closed ecosystem" πŸ™ƒ. This ambiguity is still a problem, because older implementations and ones used in closed environments may generate non-standard encodings. That can result in the JWT being misinterpreted by its recipient 🫠. A malicious sender could then use that to get past the recipient's validation checks πŸ‘Ώ.

For mitigations, see Section 3.7 🌷.

2.7. Substitution Attacks 🎭

There are attacks where one recipient is given a JWT that was meant for it and then tries to use it at a different recipient that the JWT was never meant for πŸ‘€. For example, say an OAuth 2.0 [RFC6749] access token is legitimately presented to the OAuth 2.0 protected resource it's meant for. That protected resource might then present the same access token to a different protected resource it's not meant for, trying to get in πŸšͺ. If situations like this aren't caught, the attacker can end up getting into resources they're not entitled to access 😀.

For mitigations, see Sections 3.8 and 3.9 πŸ›‘οΈ.

2.8. Cross-JWT Confusion πŸŒ€

As JWTs get used by more and more protocols in different application areas, it's getting more important to stop JWTs that were issued for one purpose from being subverted and used for another πŸ™…β€β™€οΈ. This is a specific type of substitution attack. If a JWT could be used in an app context where it might be confused with other kinds of JWTs, then mitigations MUST be employed to prevent these substitution attacks 🚨.

For mitigations, see Sections 3.8, 3.9, 3.11, and 3.12 πŸ’œ.

2.9. Indirect Attacks on the Server πŸŽ€

Okay so bestie, a bunch of JWT claims get used by the receiver to do lookups, like database searches and LDAP searches πŸ”. Other claims are URLs that the server goes and fetches too. An attacker can totally abuse any of these as a way in for injection attacks or server-side request forgery (SSRF) attacks πŸ’€ Scary, I know!

For the fixes, see Section 3.10 πŸ›‘οΈ

3. Best Practices ✨

The best practices below are what practitioners should apply to take down the threats from the section right before this one πŸ’ͺ

3.1. Perform Algorithm Verification πŸ”

Libraries MUST let the caller set up a supported list of algorithms, and they MUST NOT touch any other algorithms when doing cryptographic operations πŸ™…β€β™€οΈ The library MUST make sure that the alg or enc header names the same algorithm that's actually used for the operation. On top of that, each key MUST be used with exactly one algorithm, and this MUST be checked when the operation happens. Non-negotiable, periodt! πŸ’…

3.2. Use Appropriate Algorithms πŸ¦‹

As Section 5.2 of [RFC7515] puts it, "it is an application decision which algorithms may be used in a given context. Even if a JWS can be successfully validated, unless the algorithm(s) used in the JWS are acceptable to the application, it SHOULD consider the JWS to be invalid."

So applications MUST only allow cryptographically current algorithms that meet the app's security requirements πŸ”’ This list changes over time, because new algorithms show up and old ones get deprecated when people find cryptographic weaknesses. That's why applications MUST be designed for cryptographic agility, so swapping algorithms doesn't wreck everything ✨

That said, if a JWT is already cryptographically protected end-to-end by a transport layer, such as TLS using cryptographically current algorithms, there may be no need to add another layer of cryptographic protection to the JWT 🌸 In those cases, using the "none" algorithm can be perfectly fine. The "none" algorithm should only be used when the JWT is cryptographically protected by other means. JWTs using "none" often show up in contexts where the content is only optionally signed, so the URL-safe claims representation and processing can stay the same whether it's signed or not πŸ“ Even so, JWT libraries SHOULD NOT generate JWTs using "none" unless the caller explicitly asks them to. Likewise, JWT libraries SHOULD NOT consume JWTs using "none" unless the caller explicitly requests it πŸ™ˆ

Applications SHOULD follow these algorithm-specific recommendations πŸ‘‘

  • Avoid all RSA-PKCS1 v1.5 encryption algorithms ([RFC8017], Section 7.2), and prefer RSAES-OAEP ([RFC8017], Section 7.1) instead πŸ’–
  • Elliptic Curve Digital Signature Algorithm (ECDSA) signatures [ANSI-X962-2005] require a unique random value for every message that gets signed. If even a few bits of that random value are predictable across multiple messages, the security of the signature scheme may be compromised. In the worst case, an attacker may be able to recover the private key 😱 To counter these attacks, JWT libraries SHOULD implement ECDSA using the deterministic approach defined in [RFC6979]. This approach is completely compatible with existing ECDSA verifiers, so it can be implemented without new algorithm identifiers being required ✨

3.3. Validate All Cryptographic Operations πŸ”

All cryptographic operations used in the JWT MUST be validated, and the entire JWT MUST be rejected if any one of them fails to validate. This goes for JWTs with a single set of Header Parameters and also for Nested JWTs, where both the outer and inner operations MUST be validated using the keys and algorithms supplied by the application. No skipping, no shortcuts! 🚫

3.4. Validate Cryptographic Inputs πŸ§ͺ

Some cryptographic operations, like Elliptic Curve Diffie-Hellman key agreement ("ECDH-ES"), take inputs that might contain invalid values πŸ‘€ That includes points that aren't on the specified elliptic curve, or other invalid points (e.g., [Valenta], Section 7.1). The JWS/JWE library itself should validate these inputs before using them, or it must use underlying cryptographic libraries that do so (or both!) 🫢

Elliptic Curve Diffie-Hellman Ephemeral Static (ECDH-ES) ephemeral public key (epk) inputs should be validated according to the recipient's chosen elliptic curve. For the NIST prime-order curves P-256, P-384, and P-521, validation MUST be performed according to Section 5.6.2.3.4 (ECC Partial Public-Key Validation Routine) of "Recommendation for Pair-Wise Key-Establishment Schemes Using Discrete Logarithm Cryptography" [nist-sp-800-56a-r3]. If the X25519 or X448 [RFC8037] algorithms are used, then the security considerations in [RFC8037] apply ⭐

3.5. Ensure Cryptographic Keys Have Sufficient Entropy 🌈

The Key Entropy and Random Values advice in Section 10.1 of [RFC7515] and the Password Considerations in Section 8.8 of [RFC7518] MUST be followed πŸ“– In particular, human-memorizable passwords MUST NOT be used directly as the key to a keyed-MAC algorithm like "HS256". Also, passwords should only be used to perform key encryption, rather than content encryption, as described in Section 4.8 of [RFC7518]. Heads up: even when a password is used for key encryption, password-based encryption is still open to brute-force attacks 😭

3.6. Avoid Compression of Encryption Inputs πŸ—œοΈ

Compressing data SHOULD NOT be done before encryption, because compressed data often reveals information about the plaintext πŸ™Š

3.7. Use UTF-8 🌏

[RFC7515], [RFC7516], and [RFC7519] all specify that UTF-8 be used for encoding and decoding the JSON in Header Parameters and JWT Claims Sets. This also lines up with the latest JSON specification, [RFC8259] πŸ€ Implementations and applications MUST do this, and MUST NOT use or allow other Unicode encodings for these purposes 🚫

3.8. Validate Issuer and Subject πŸͺͺ

When a JWT contains an "iss" (issuer) claim, the application MUST validate that the cryptographic keys used for the cryptographic operations in the JWT belong to the issuer. If they don't, the application MUST reject the JWT πŸ›‘

How you figure out which keys an issuer owns is up to the application. As one example, OpenID Connect [OpenID.Core] issuer values are "https" URLs that point to a JSON metadata document, which contains a "jwks_uri" value. That value is an "https" URL where the issuer's keys get retrieved as a JWK Set [RFC7517]. This same mechanism is used by [RFC8414]. Other applications may bind keys to issuers in different ways 🧩

Similarly, when the JWT contains a "sub" (subject) claim, the application MUST validate that the subject value corresponds to a valid subject and/or issuer-subject pair at the application. This may include confirming that the issuer is trusted by the application. If the issuer, the subject, or the pair is invalid, the application MUST reject the JWT πŸ™…β€β™€οΈ

3.9. Use and Validate Audience 🎯

If the same issuer can issue JWTs meant for more than one relying party or application, the JWT MUST contain an "aud" (audience) claim. That claim is what lets someone figure out whether the JWT is being used by an intended party, or whether an attacker swapped it in at an unintended one πŸ•΅οΈβ€β™€οΈ

In those cases, the relying party or application MUST validate the audience value. If the audience value is missing or isn't associated with the recipient, it MUST reject the JWT πŸšͺ

3.10. Do Not Trust Received Claims πŸ™…β€β™€οΈ

The "kid" (key ID) header is used by the relying application to do key lookup. Applications should make sure this doesn't create SQL or LDAP injection vulnerabilities, by validating and/or sanitizing the received value 🧼

Same vibes with a "jku" (JWK set URL) or "x5u" (X.509 URL) header. Blindly following one of those, which can contain an arbitrary URL, could lead to server-side request forgery (SSRF) attacks 😬 Applications SHOULD protect against such attacks, for example by matching the URL against a whitelist of allowed locations and making sure no cookies get sent in the GET request πŸ”’

3.11. Use Explicit Typing 🏷️

Sometimes one kind of JWT gets confused for another. If a particular kind of JWT is prone to that, the JWT can include an explicit JWT type value, and the validation rules can say to check the type. That mechanism can prevent the confusion. Explicit JWT typing is done with the "typ" Header Parameter. For instance, the [RFC8417] specification uses the application/secevent+jwt media type to explicitly type Security Event Tokens (SETs) πŸ“¨

Per the definition of "typ" in Section 4.1.9 of [RFC7515], it is RECOMMENDED that the "application/" prefix be left off the "typ" value. So, for example, the "typ" value used to explicitly include a type for a SET SHOULD be "secevent+jwt". When explicit typing is used for a JWT, it is RECOMMENDED that a media type name of the format application/example+jwt be used, where example is replaced by the identifier for that specific kind of JWT πŸŽ€

When applying explicit typing to a Nested JWT, the "typ" Header Parameter containing the explicit type value MUST be present in the inner JWT of the Nested JWT (the JWT whose payload is the JWT Claims Set). In some cases, the same "typ" Header Parameter value will also be present in the outer JWT, to explicitly type the whole Nested JWT πŸͺ†

Note that explicit typing may not achieve disambiguation from existing kinds of JWTs, since the validation rules for existing kinds often don't look at the "typ" Header Parameter value πŸ€·β€β™€οΈ Still, explicit typing is RECOMMENDED for new uses of JWTs 🌟

3.12. Use Mutually Exclusive Validation Rules for Different Kinds of JWTs πŸ’…

okay so bestie, every JWT application gets its own profile πŸŽ€ that spells out which claims are REQUIRED, which are optional, and what validation rules go with each one. And if the same issuer can mint more than one kind of JWT πŸ‘€, the validation rules for those kinds MUST be mutually exclusive, so each one rejects the wrong kind. No mixing up tokens from one context and sneaking them into another. πŸ™…β€β™€οΈ

To stop JWTs getting swapped between contexts, app devs can use a bunch of strategies 🧚:

  • Use explicit typing for different kinds of JWTs. Then the distinct "typ" values can tell the different kinds apart. 🏷️
  • Use different sets of required claims or different required claim values. Then the validation rules for one kind of JWT will reject ones with different claims or values. πŸ“
  • Use different sets of required Header Parameters or different required Header Parameter values. Then the validation rules for one kind of JWT will reject ones with different Header Parameters or values. πŸŽ€
  • Use different keys for different kinds of JWTs. Then the keys used to validate one kind of JWT will fail to validate the other kinds. πŸ”‘
  • Use different "aud" values for different uses of JWTs from the same issuer. Then audience validation will reject JWTs that got substituted into the wrong context. πŸ¦‹
  • Use different issuers for different kinds of JWTs. Then the distinct "iss" values can segregate the different kinds. 🌈

ngl, the best combo of types, required claims, values, Header Parameters, key usages, and issuers to tell JWTs apart is going to be pretty app-specific 🫢, because JWT usage is SO diverse. As we covered in Section 3.11, for new JWT applications, using explicit typing is RECOMMENDED. Main character energy πŸ‘‘βœ¨

4. Security Considerations ✨

This entire document is about security considerations when implementing and deploying JSON Web Tokens. Literally the whole thing 😭πŸ”₯

5. IANA Considerations πŸŽ€

This document has no IANA actions. 🌸

6. References πŸ“š

6.1. Normative References πŸ’œ

6.2. Informative References 🌷

Acknowledgements 🫢

Thanks to Antonio Sanso for bringing the "ECDH-ES" invalid point attack to the attention of JWE and JWT implementers. Tim McLean published the RSA/HMAC confusion attack [McLean]. Thanks to Nat Sakimura for advocating the use of explicit typing. Thanks to Neil Madden for his numerous comments, and to 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 for their reviews. πŸ’–

Authors' Addresses πŸ’Œ

Yaron Sheffer Intuit

Email: yaronf.ietf@gmail.com


Dick Hardt

Email: dick.hardt@gmail.com


Michael B. Jones Microsoft

Email: mbj@microsoft.com URI: https://self-issued.info/

πŸ“¦ where this page comes from