JSON Web Token

JWT logo. Source: Woloski 2015.
JWT logo. Source: Woloski 2015.

JSON is a data format commonly used in web applications. JSON Web Token (JWT) is a mechanism that brings security to JSON data.

JSON grew in adoption from the mid-2000s. This influenced the adoption of JWT. Compared to alternatives such as XML or SAML, app developers found JWT easier to implement and use. JWTs are less verbose and more secure. By the late 2010s, JWTs were widely used in the world of cloud computing and microservices.

JWT is available in two formats: JSON Web Signature (JWS) and JSON Web Encryption (JWE). JWS offers protection against data tampering (integrity). JWE prevents others from reading the data (confidentiality). Moreover, developers have a choice of various keys and algorithms to protect JSON data in either of these formats.

IETF has published the main RFCs that cover JWTs. There are also plenty of open source implementations in many languages.

Discussion

  • How does JWT bring security to the web?
    A typical authorization use case of JWT. Source: Calandra 2019, fig. 5.
    A typical authorization use case of JWT. Source: Calandra 2019, fig. 5.

    Consider an application consisting of many services exposed to clients via APIs. We certainly don't want clients to authenticate with each service. Authentication is done by a specific service or server. Once authenticated successfully, the client should be able to access any of the services without further authentication. This is where JWTs can help.

    For example, in AWS, Amazon Cognito does authentication. An authenticated client is issued a JWT. Whenever the client makes an API request, it presents this token. The API gateway validates the token before allowing the client to access the requested service. Thus, all relevant information is within the JWT. The API gateway need not contact the authentication server to determine if the client should be allowed access.

    For authorized access, privileges can be set within the token. For example, the name-value pair admin:true could be set to allow deletion of records and other admin operations. Moreover, such privileges are set when the token is issued. The client or third-party hackers can't tamper with the token.

  • What are some use cases where JWT can be used?

    The common use case of JWTs is authorization. For example, APIs often require an access token and this could be a JWT. Systems implementing Single Sign-On (SSO) can issue JWTs to allow the user to access various services.

    Assume a server authenticates a user and issues a single-use short-lived JWT. User uses this token to download a file from another server. In this example, JWT temporarily authorizes the user to download a protected resource. In microservices architecture, JWTs are used to pass authorization across services. OAuth 2.0 access tokens are JWTs.

    JWTs can be used for authentication. For example, in OpenID Connect (OIDC), users login with a JWT. Another example is to authenticate a SOAP request with JWT rather than SAML2 assertion. In Oracle Cloud, API gateway authenticates an API client based on the JWT it receives. Once validated, claims in the token are used to authorize the client. JWTs can be used to authenticate SPAs.

    Due to its protection against tampering and snooping, JWTs are a means to exchange information securely.

  • What are the main components of a signed JWT?
    A signed JWT has three components. Source: Danyal 2020.
    A signed JWT has three components. Source: Danyal 2020.

    A JWT has two essential components: header and payload. In practice, JWTs are signed, that is, they include a signature. This is what we call JWS. Thus, the three main components of a signed JWT are:

    • Header: Specifies the type of token (typically JWT) and the algorithm used.
    • Payload: The main content of the token that includes a set of claims.
    • Signature: This is computed from header and payload to protect the integrity of the token.

    Header and payload are JSON objects. However, these are not transmitted as such. They are Base64-URL encoded, which is similar to Base64 encoding except that characters special to URLs are replaced: + becomes -, / becomes _.

    Signature is computed as BASE64URL(UTF8(JWS Protected Header)) || '.' || BASE64URL(JWS Payload) and then Base64-URL encoded.

    The JWS is constructed by concatenating header, payload, and signature. Period character separates the fields. We can represent this as BASE64URL(UTF8(JWS Protected Header)) || '.' || BASE64URL(JWS Payload) || '.' || BASE64URL(JWS Signature). This format of JWS is called JWS Compact Serialization. There's also JWS JSON Serialization that can have multiple signatures.

  • What are JWT claims and how to specify such claims?
    Claim set in an example JWT issued by Amazon Cognito. Source: AWS 2020.
    Claim set in an example JWT issued by Amazon Cognito. Source: AWS 2020.

    The payload of a JWT has a set of claims. A claim is a name-value pair. It states a fact about the token and its subject such as username or email address. Claims are not mandatory since JWTs are meant to be compact. It's up to applications to include claims that matter.

    Some claim names are registered with IANA. Examples include "iss" (issuer), "sub" (subject), and "aud" (audience). Some registered claim names are of datetime type: "exp" (token expires at this time), "nbf" (token can't be used before this time), and "iat" (time when token was issued). For example, "exp":1300819380 says that the token expires at the specified timestamp.

    There are also public or private claim names. These are application specific and their semantics are agreed between producer and consumer of the token. Public names must be collision-resistant. For example, a name based on domain name or Universally Unique IDentifier (UUID) is unlikely to collide with another public name.

  • Is it possible to encrypt the payload in a JWT?
    Illustrating a nested JWT. Source: Kawasaki 2017.
    Illustrating a nested JWT. Source: Kawasaki 2017.

    A signed token can be read by anyone. The purpose of signature is to prevent hackers from tampering with the header or payload. Any changes to these would result in a different signature, which the attacker can't create without the secret key. Such tampering would cause a failure during signature verification.

    If the intention is to send sensitive payload, signature alone is inadequate. There's a need to encrypt the payload. This is where JWE format becomes relevant.

    Encryption of content uses symmetric keys. However, these keys need not be shared in advance. They can be generated dynamically and exchanged within JWE. However, the shared symmetric key is encrypted using asymmetric private-public key pair.

    It's also possible to do both, such as a JWS within a JWE or vice versa. Such as token is called Nested JWT. For example, we could create a JWS as usual and encrypt it. This then becomes the encrypted payload of a JWE. A simpler approach is to use only encryption algorithms mentioned in JWA since they also provide integrity protection.

  • Which are the various algorithms supported by JWT?
    Use of symmetric and asymmetric keys for JWS. Source: Pragmatic Web Security 2020.
    Use of symmetric and asymmetric keys for JWS. Source: Pragmatic Web Security 2020.

    Both symmetric and asymmetric algorithms are supported for signing and encrypting tokens. In fact, this support for a variety of algorithms is perhaps one reason for the wider adoption of JWTs.

    For JWS, at the minimum, an implementation should support HMAC using SHA-256. This uses a shared symmetric key. For the "alg" header parameter, its value is "HS256". Apart from HMAC for signature, we can have digital signatures using asymmetric keys with RSASSA-PKCS1-v1_5, ECDSA, or RSASSA-PSS. Signing is done with the private key. Verification happens with the public key.

    For content encryption in JWE, at the minimum, an implementation should support A128CBC-HS256 and A256CBC-HS512. A128CBC-HS256 does AES encryption in CBC mode with 128-bit IV value, plus HMAC authentication using SHA-256 and truncating HMAC to 128 bits. Encryption key is called Content Encryption Key (CEK).

    The CEK itself is encrypted using other algorithms and included in the JWE. Some of these are RSA-OAEP, A128KW, ECDH-ES, ECDH-ES+A128KW, and more. It's also possible to use a shared symmetric key as CEK.

  • Can I use JWTs as a replacement to session objects?

    With session objects, server maintains the state about each logged-in user, who gets a session ID via a HTTP cookie. Subsequent requests contain the session ID. Server uses it to retrieve the session object and serve the client. Thus, stateless HTTP calls are strung together into a stateful session.

    With JWTs, server doesn't need to store session state. All relevant information is contained in the JWT. This also makes it convenient to deploy on a distributed architecture. One server might issue the JWT. Subsequent client requests could be served by another server via a load balancer.

    While JWTs seem attractive, a JWT takes more space compared to a session ID. Even without session objects, most client requests will still need access to the database, implying that JWTs don't improve performance. Most web frameworks automatically sign session cookies, implying that signing a JWT isn't really an advantage. JWTs stored on local storage can be less secure. We can't invalidate individual JWTs or deal with stale claims in the token. For these reasons, use of JWTs as an alternative to session IDs is not recommended.

  • Which are the known vulnerabilities of JWT?
    JWT without audience misused to gain access to Org2. Source: Peyrott 2018, fig. 8.2.
    JWT without audience misused to gain access to Org2. Source: Peyrott 2018, fig. 8.2.

    Most attacks on JWT are due to implementations rather than its design.

    Some early implementations used the "alg" value in header to verify the signature. An attacker could set the value to "none"; or change "RS256" to "HS256" and use the public key as the shared secret key to generate a valid signature. Based on the "alg" value, the consumer would skip verification or incorrectly find that the signature is valid.

    Brute force attacks on HS256 are simple if the shared secret key is too short. Another possible oversight in implementations is not verifying the claims or not including adequate claims. For example, a token without audience is issued to Org1 but the attacker could present the same token to Org2 can gain access if that username exists in both organizations.

    Don't store sensitive information in JWS, that is, in unencrypted form. Don't also assume that encrypted data can't be tampered with.

    RFC8725 details many vulnerabilities and best practices to overcome the same.

  • What are some best practices when using JWT?

    Pick strong keys. These are often long and created by cryptographic-quality pseudo-random number generators (PRNGs). Don't use human-readable shared secret keys. For federated identity, or when third-party services are involved, it's inconvenient and unsafe to use a shared secret. Instead, use public-private key pair.

    Don't rely on the header to select the algorithm for verifying the signature. Use libraries that allow for explicit selection of algorithm.

    It's a good practice to verify applicable claims. For example, verify that token has not expired. In AWS we might verify that the audience claim matches the app client ID created in the Amazon Cognito user pool. Where nested tokens are used, verify signature on all tokens, not just on the outermost token.

    Use different validation rules for each token. Avoid key reuse for different tokens. For example, we could use different secret keys for each subsystem. Using "kid" claim, we could identify which secret key is used by the token.

    Keep the lifetime of tokens short, says for a few minutes or hours. In addition, we could include a nonce in the token to prevent replay attacks (which is what OpenID Connect does).

  • Could you mention some resources concerning JWT?

    IETF documents relevant to JWT include RFC7519: JSON Web Token (JWT), RFC7515: JSON Web Signature (JWS), RFC7516: JSON Web Encryption (JWE), RFC7517: JSON Web Key (JWK), RFC7518: JSON Web Algorithms (JWA), and RFC7797: JSON Web Signature (JWS) Unencoded Payload Option.

    IANA's JOSA page contains lists of registered header parameter names and algorithm names.

    Peyrott's JWT Handbook is worth reading. This book includes JavaScript code with explanations, which is a useful reference for developers. Another useful reference is a JWT cheatsheet published by Pragmatic Web Security.

    For a simpler approach, developers can use third-party JWT libraries. These are available in many languages. Lists of JWT implementations are available at OpenID and at jwt.io.

    Site jwt.io offers a debugger to paste a JWT and view its decoded form. Optionally, signature verification is possible if you include the secret key.

Milestones

Apr
2001

Douglas Crockford and Chip Morningstar send out what is historically the first JSON message. Since JSON is nothing more than plain JavaScript, Crockford himself states that probably this message format was in use as early as 1996. In July 2006, Crockford describes in RFC 4627 the JSON format and its MIME media type application/json.

2005

From the mid-2000s, the growth of Web 2.0 and the use REST APIs in web apps lead to wider adoption of JSON. The term AJAX itself is coined in 2005 but it's clear than AJAX is not limited to XML: JSON can be used instead. By late 2000s, with the increasing use of JSON on the web, it's recognized that standards are needed to offer security services in JSON format.

Dec
2010

As an Internet-Draft, JSON Web Token (JWT) is published at IETF. This document goes through multiple revisions, with the final draft revision appearing in December 2014. In May 2015, it becomes RFC7519.

Sep
2011

At IETF, the Javascript Object Signing and Encryption (JOSE) Working Group is formed. For better interoperability, the group aims to standardize the mechanism for integrity protection (signature and MAC), encryption, the format of keys and algorithm identifiers.

May
2015

IETF publishes RFC7519: JSON Web Token (JWT) as a Proposed Standard. Other relevant RFCs are also published the same month: RFC7515: JWS, RFC7516: JWE, RFC7517: JWK, RFC7518: JWA.

Jul
2015

On Auth0 blog, a new logo for JWT is announced along with a redesigned website at jwt.io. The blog post also notes that interest in JWT has been increasing since 2013. By now, there are 972 JWT-related GitHub repositories and 2600+ threads on StackOverflow. It's also claimed that,

If you use Android, AWS, Microsoft Azure, Salesforce, or Google then chances are that you are already using JWT.
Feb
2016
Illustrating unencoded payload specified in RFC7797. Source: Jones 2016, sec. 4.
Illustrating unencoded payload specified in RFC7797. Source: Jones 2016, sec. 4.

IETF publishes RFC7797: JSON Web Signature (JWS) Unencoded Payload Option as a Proposed Standard. Typically, JWT payload is Base64-URL encoded. This document gives the option of skipping this encoding step. For example, a payload $.02 is sent as it is rather than sending its encoded form of JC4wMg. Header parameter "b64" controls the use of this option and "crit" parameter facilitates backward compatibility.

Jul
2019
JWT is split between local storage and cookie. Source: Ideneal 2019.
JWT is split between local storage and cookie. Source: Ideneal 2019.

JWTs stored in local or session storage are vulnerable to XSS attacks. On the other hand, JWTs stored in cookies are vulnerable to CSRF attacks. One blogger proposes to mitigate the risks by storing the signature in a HttpOnly, SameSite, Secure cookie. JWT header and payload are in local storage and transferred in HTTP header as a bearer token. The application server has to assemble the complete JWT from its parts. HttpOnly cookies are inaccessible to JavaScript.

Sample Code

  • # Source: https://jose.readthedocs.io/en/latest/
    # Accessed 2020-12-07
     
    # Examples showing the use of JOSE Python package
     
    # -------------------------------------------------
    # JWS signed with HS256
    # -------------------------------------------------
    import jose
     
    claims = {
        'iss': 'http://www.example.com',
        'exp': int(time()) + 3600,
        'sub': 42,
    }
     
    jwk = {'k': 'password'}
     
    jws = jose.sign(claims, jwk, alg='HS256')
    # JWS(header='eyJhbGciOiAiSFMyNTYifQ',
    # payload='eyJpc3M...',
    # signature='WYApAiwiKd-eDClA1fg7XFrnfHzUTgrmdRQY4M19Vr8')
     
    # issue the compact serialized version to the clients. this is what will be
    # transported along with requests to target systems.
     
    jwt = jose.serialize_compact(jws)
    # 'eyJhbGciOiAiSFMyNTYifQ.eyJpc3M....WYApAiwiKd-eDClA1fg7XFrnfHzUTgrmdRQY4M19Vr8'
     
    jose.verify(jose.deserialize_compact(jwt), jwk, 'HS256')
    # JWT(header={u'alg': u'HS256'},
    # claims={u'iss': u'http://www.example.com', u'sub': 42, u'exp': 1395674427})
     
    # -------------------------------------------------
    # JWE: content encrypted with A128CBC-HS256 and
    #      CEK encrypted with RSA-OAEP
    # -------------------------------------------------
    import jose
    from time import time
    from Crypto.PublicKey import RSA
     
    # key for demonstration purposes
    key = RSA.generate(2048)
     
    claims = {
        'iss': 'http://www.example.com',
        'exp': int(time()) + 3600,
        'sub': 42,
    }
     
    # encrypt claims using the public key
    pub_jwk = {'k': key.publickey().exportKey('PEM')}
     
    jwe = jose.encrypt(claims, pub_jwk)
    # JWE(header='eyJhbGc...',
    # cek='SsLgP2b...',
    # iv='Awelp3ryBVpdFhRckQ-KKw',
    # ciphertext='1MyZ-3n...',
    # tag='Xccck85XZMvG-fAJ6oDnAw')
     
    # issue the compact serialized version to the clients. this is what will be
    # transported along with requests to target systems.
     
    jwt = jose.serialize_compact(jwe)
    # 'eyJhbGc....SsLgP2b....Awelp3ryBVpdFhRckQ-KKw.1MyZ-3n....Xccck85XZMvG-fAJ6oDnAw'
     
    # decrypt on the other end using the private key
    priv_jwk = {'k': key.exportKey('PEM')}
     
    jwt = jose.decrypt(jose.deserialize_compact(jwt), priv_jwk)
    # JWT(header={u'alg': u'RSA-OAEP', u'enc': u'A128CBC-HS256'},
    # claims={u'iss': u'http://www.example.com', u'sub': 42, u'exp': 1395606273})

References

  1. Auth0 Docs. 2020. "JSON Web Tokens." Documentation, Auth0. Accessed 2020-12-05.
  2. AWS. 2020. "How can I decode and verify the signature of an Amazon Cognito JSON Web Token?" Knowledge Center, AWS, Amazon, September 14. Accessed 2020-12-05.
  3. Broeckelmann, Robert. 2017. "JWT Use Cases." Medium, July 13. Accessed 2020-12-05.
  4. Calandra, Mariano. 2019. "Why do we need the JSON Web Token (JWT) in the modern web?" The Startup, on Medium, September 6. Accessed 2020-12-05.
  5. Copes, Flavio. 2018. "JWT authentication: When and how to use it." Blog, LogRocket, October 11. Accessed 2020-12-05.
  6. Crockford, D. 2006. "The application/json Media Type for JavaScript Object Notation (JSON)." RFC 4627, IETF, July. Accessed 2020-12-05.
  7. Danyal, Muhammad. 2020. "How To Validate a JWT Token." DataSeries, on Medium, April 15. Accessed 2020-12-05.
  8. De Ryck, Philippe. 2019. "The Hard Parts of JWT Security Nobody Talks About." Blog, Ping Identity, January 10. Accessed 2020-12-05.
  9. IANA. 2020. "JSON Object Signing and Encryption (JOSE)." IANA, November 2. Created 2015-01-23. Accessed 2020-12-05.
  10. Ideneal. 2019. "Securing Authentication in a SPA Using JWT Token — The coolest way." Medium, July 8. Accessed 2020-12-05.
  11. IETF Datatracker. 2016a. "Documents: Javascript Object Signing and Encryption (jose)." JOSE WG, IETF Datatracker, August 22. Accessed 2020-12-05.
  12. IETF Datatracker. 2016b. "History: Javascript Object Signing and Encryption (jose)." JOSE WG, IETF Datatracker, August 22. Accessed 2020-12-05.
  13. IETF Datatracker. 2016c. "About: Javascript Object Signing and Encryption (jose)." JOSE WG, IETF Datatracker, August 22. Accessed 2020-12-05.
  14. Java Brains. 2019. "What is JWT authorization really about." Java Brains, on YouTube, October 2. Accessed 2020-12-05.
  15. Jones, M. 2015. "JSON Web Algorithms (JWA)." RFC 7518, IETF, May. Accessed 2020-12-05.
  16. Jones, M. 2016. "JSON Web Signature (JWS) Unencoded Payload Option." RFC 7797, IETF, February. Accessed 2020-12-05.
  17. Jones, M., J. Bradley, and N. Sakimura. 2014. "JSON Web Token (JWT)." draft-ietf-oauth-json-web-token-32, IETF, December 9. Accessed 2020-12-05.
  18. Jones, M., J. Bradley, and N. Sakimura. 2015a. "JSON Web Token (JWT)." RFC 7519, IETF, May. Accessed 2020-12-05.
  19. Jones, M., J. Bradley, and N. Sakimura. 2015b. "JSON Web Signature (JWS)." RFC 7515, IETF, May. Accessed 2020-12-05.
  20. jwt.io. 2020a. "Homepage." jwt.io. Accessed 2020-12-05.
  21. jwt.io. 2020b. "Introduction to JSON Web Tokens." jwt.io. Accessed 2020-12-05.
  22. Kawasaki, Takahiko. 2017. "Understanding ID Token." Medium, November 6. Accessed 2020-12-05.
  23. OpenID. 2020. "JWT, JWS, JWE, JWK, and JWA Implementations." OpenID. Accessed 2020-12-05.
  24. Oracle Cloud Docs. 2020. "Using JSON Web Tokens (JWTs) to Add Authentication and Authorization to API Deployments." Oracle Cloud Infrastructure Documentation, Oracle Cloud. Accessed 2020-12-05.
  25. Peyrott, Sebastián E. 2018. "JWT Handbook." Version 0.14.1, Auth0. Accessed 2020-12-05.
  26. Pragmatic Web Security. 2020. "JSON Web Tokens (JWT)." Cheatsheet, Version 2020.001, Pragmatic Web Security. Accessed 2020-12-05.
  27. rdegges. 2017. "Why JWTs Suck as Session Tokens." Scotch.io, July 11. Accessed 2020-12-05.
  28. Sheffer, Y., D. Hardt, and M. Jones. 2020. "JSON Web Token Best Current Practices." RFC 8725, IETF, February. Accessed 2020-12-05.
  29. Siriwardena, Prabath. 2016a. "Securing Microservices (Part I)." Facilelogin, on Medium, April 12. Accessed 2020-12-05.
  30. Siriwardena, Prabath. 2016b. "JWT, JWS and JWE for Not So Dummies! (Part I)." Facilelogin, on Medium, April 27. Accessed 2020-12-05.
  31. Slootweg, Sven. 2016. "Stop using JWT for sessions." Blog, joepie91's Ramblings, June 13. Accessed 2020-12-05.
  32. Target, Sinclair. 2017. "The Rise and Rise of JSON." 0b10: Two-Bit History, September 21. Accessed 2020-12-05.
  33. Vogel, Lucas. 2017. "JSON vs. XML: The battle for format supremacy may be wasted energy." SD Times, July 26. Accessed 2020-12-05.
  34. Websecurity. 2017. "Hacking JSON Web Tokens." Blog, Websecurity, February 9. Accessed 2020-12-05.
  35. Woloski, Matias. 2015. "JWT: 2 years later." Blog, Auth0, July 21. Accessed 2020-12-05.

Further Reading

  1. Peyrott, Sebastián E. 2018. "JWT Handbook." Version 0.14.1, Auth0. Accessed 2020-12-05.
  2. Jones, M., J. Bradley, and N. Sakimura. 2015a. "JSON Web Token (JWT)." RFC 7519, IETF, May. Accessed 2020-12-05.
  3. Pragmatic Web Security. 2020. "JSON Web Tokens (JWT)." Cheatsheet, Version 2020.001, Pragmatic Web Security. Accessed 2020-12-05.
  4. Sheffer, Y., D. Hardt, and M. Jones. 2020. "JSON Web Token Best Current Practices." RFC 8725, IETF, February. Accessed 2020-12-05.
  5. Siriwardena, Prabath. 2016b. "JWT, JWS and JWE for Not So Dummies! (Part I)." Facilelogin, on Medium, April 27. Accessed 2020-12-05.
  6. Lin, Joyce. 2018. "Using JWT to authenticate and authorize requests in Postman." Dev.to, November 28. Accessed 2020-12-05.

Article Stats

Author-wise Stats for Article Edits

Author
No. of Edits
No. of Chats
DevCoins
4
0
1646
2467
Words
2
Likes
619
Hits

Cite As

Devopedia. 2020. "JSON Web Token." Version 4, December 7. Accessed 2021-03-28. https://devopedia.org/json-web-token
Contributed by
1 author


Last updated on
2020-12-07 17:34:12
  • Cryptography
  • Cryptographic Hash Function
  • API Security
  • OAuth
  • Single Sign-On
  • Identity and Access Management