DEV Community

Cover image for What are JWT?
Thomas Broyer
Thomas Broyer

Posted on • Originally published at blog.ltgt.net

What are JWT?

This is a translation of an article I wrote for our internal knowledge base at work, and that we later decided to publish (in French).


This article's goal is to present what JWTs are, whenever you face them. As we'll see, you won't deliberately choose to use JWTs in a project, and more importantly: you won't use JWTs as session tokens.

What is it?

JSON Web Token (JWT) is a compact, URL-safe means of representing data to be transferred between two parties. The data is encoded as a JSON object that can be signed and/or encrypted.

This is, paraphrased, the definition from the IETF standard that defines it (RFC 7519).

What's the point? What's the use case?

So the goal is to transfer data, with some guarantees (or none, by the way): authenticity, integrity, even possibly confidentiality (if the message is encrypted). There are therefore many possible uses.

JWT is thus used in OpenID Connect to encode the ID Token that forwards to the application information on the authentication process that took place at the identity server. OpenID Connect also uses JWT to encode aggregated claims: information from other identity servers, for which we'll want to verify the authenticity and integrity.

A JWT might be used to authenticate to a server, such as with the OAuth 2 JWT Bearer (RFC 7523).

Still in OAuth 2 land, access tokens could themselves be JWTs (RFC 9068), authorization request parameters could be encoded as a JWT (RFC 9101), as well as token introspection responses (IETF draft: JWT Response for OAuth Token Introspection), and finally dynamic client registration uses a JWT to identify the software of which an instance attempts to register (so-called software statements of RFC 7591).

How does it work?

A JWT is composed of at least 2 parts, separated with a . (dot), the first one always being the header. Each part is always encoded as base64url, a variant of Base 64 with the + and / characters (that have special meaning in URLs) replaced with - and _ respectively, and without the trailing =.

There are two types of JWTs: JSON Web Signature (JWS, defined by RFC 7515), and JSON Web Encryption (JWE, defined by RFC 7516). The most common case is the JWS, composed of 2 or 3 parts: the header, the payload, and optionally the signature. JWEs are rarer (and more complex) so I won't talk about them here.

The header, common to both types, describes the type of JWT (JWS or JWE) as well as the different signature, MAC, or encryption algorithms being used (codified by RFC 7518), along with other useful information, as a JSON object.

In the case of JWS, we'll find the signature or MAC algorithm, possibly a key identifier (whenever multiple keys can be used, e.g. to allow for key rotation), or even a URL pointing to information about the keys (in JWKS format, defined by RFC 7517), etc.

In the case of JWS, the payload will generally be a JSON object with the transfered data (but technically could be another JWT).

The third part is the signature or MAC. This part is absent if the header says the JWT is unprotected ("alg":"none").

For debugging, one can use the JWT Debugger by Auth0 to decode JWTs (beware not to use it with sensitive data, only on JWTs coming from test servers).

⚠️ JWT being almost always used in security-related contexts, handle them with care, specifically when it comes to their cryptographical components.

One MUST use dedicated libraries to manipulate JWTs, and be careful to use them correctly to avoid introducing vulnerabilities.

RFC 8725 has a set of best practices when manipulating and using JWTs.

Criticism

Numerous security experts, among them cryptographers, vehemently criticize JWTs and advise against their use.

The main criticism relates to its complexity, even though it could look simple to developers:

  • first, you need to know how to decode UTF-8 and JSON ; that's as many sources of bugs (and potential vulnerabilities).
  • and of course because it's a generic format capable of signing and/or encrypting, or even not protecting anything at all ("alg":"none"), with a list of supported algorithms as long as your arm, you have to handle many cases (even if only to reject them).

As a result, a number of vulnerabilities have been identified; among them (identified as soon as March 2015):

  • As the JWT itself declares the algorithm used to sign or encrypt it, software that receives it needs to partly trust it, or correctly check the used algorithm against a list of authorized algorithms. Because of its apparent simplicity, many libraries came out that didn't do those necessary checks and readily accepted unprotected JWTs ("alg":"none"), allowing an attacker to use any JWT, without authenticity or integrity check. And as incredible as it may seem, we still find vulnerable applications nowadays!

Note: in the same way, the header can directly include the public key to be used to verify the signature. Using it will prove the integrity of the JWT, but not its authenticity as the signature could have been generated by anyone.

  • Another attack involes using the public key intended to verify an asymmetric signature ("alg":"RS256" or "alg":"ES256") as a MAC key ("alg":"HS256"): the application receiving the JWT could then mistakenly validate the MAC and allow the JWT in. Anybody could then create a JWS that would be accepted by the application, when that one thinks it's verifying an asymmetric signature.

This vulnerability could be due to a misuse of the library used to verify JWTs, but also in some cases directly to its API that cannot tell between a public key and a shared secret (generally for the sake of making it easy to use).

Aside: despite ID Tokens in OpenID Connect being JWTs, you won't actually need to verify their signature as you generally get them through HTTPS, that already guarantees authenticity and integrity (and confidentiality), which saves us from a whole class of vulnerabilities.

Another criticism is due to the misuse of JWT, most often by ignorance or lack of expertise in software security: validity of a JWT is directly verifiable, without the need for a database of valid tokens or a validation service (authenticity and integrity are verifiable, so the validity period contained within in the JWT are reliable), but it makes the JWT impossible to revoke (unless you add such a mecanism –possibly based on the jti claim, initially designed to protect against replay attacks– going against the whole reason for which JWT was chosen in the first place). If a JWT is used as a session token for example, it then becomes impossible to sign out or terminate a session. In most use cases (in the specifications), a JWT is validated and used as soon as it's received from the issuer, so revocation is not even an issue. It's when a JWT is stored by the receiver for a later use that the problem arises (such as with a session token or an access token).

Some articles critical of JWT:

Top comments (4)

Collapse
 
kremsku profile image
kremsku

Very good and valid points! I would summarise this as: "JWT's can be used in a secure way, but can also be implemented in a totally incorrect way".

As in many technologies, the technology itself doesn't guarantee security, it is the implementation that matters.

Collapse
 
arietimmerman profile image
Arie Timmerman

Nice overview with many interesting things to consider.

I do disagree with you on this part however:

Aside: despite ID Tokens in OpenID Connect being JWTs, you won't actually need to verify their signature as you generally get them through HTTPS, that already guarantees authenticity and integrity (and confidentiality), which saves us from a whole class of vulnerabilities.

Here HTTPS only guarantees that the ID Token has been transmitted securely between the end user's user agent and the OpenID Connect client (website). You must still verify the JWT in order to confirm it was issued legitimately from your trusted identity provider. Omitting this introduces many major security risks.

Collapse
 
tbroyer profile image
Thomas Broyer

transmitted securely between the end user's user agent and the OpenID Connect client (website)

Which flow are you talking about? With the authorization code flow (which you should use over anything else), the ID Token comes right from the IdP in the token response, so HTTPS guarantees both authenticity and integrity (and confidentiality).

If you're using an hybrid flow or implicit flow (but you shouldn't) then I agree you must validate the ID Token signature, as it comes from an insecure channel (redirect or possibly form post)

Collapse
 
arietimmerman profile image
Arie Timmerman

You are right with respect to the authorization code flow, and that is indeed the flow that should be used always. I checked the specifications and it even explicitly mentions that there is no need to verify the JWT signature. To be honest, I never really realized this.

If the ID Token is received via direct communication between the Client and the Token Endpoint (which it is in this flow), the TLS server validation MAY be used to validate the issuer in place of checking the token signature. The Client MUST validate the signature of all other ID Tokens according to JWS [JWS] using the algorithm specified in the JWT alg Header Parameter. The Client MUST use the keys provided by the Issuer.