The usual way to manage users’ sessions in a PHP application is to use session cookies, named “PHPSESSID” by default. When a user connects to the application, it generates a unique session identifier, that is stored on the server and then returned to the client with the “Set-Cookie” header. With that, the session cookie is stored on the web browser.
Cookies are designed to be systematically sent back to the server, on every request. That solution is a good way to handle usual user login and logout scenarios.

However, this type of mechanics does not allow multiple platforms or applications to easily authenticate a user with a single session. Also, the server needs to keep the session’s state and data in its memory.

JWT tokens are “stateless”, meaning that session information is not stored server-side. On top of the great gain in terms of saved memory on the server, JWT tokens can be used to authenticate users on multiple applications. To do that, the different applications will need to share the same private key to sign and verify tokens. Users will therefore be able to authenticate one single time on the application that manages user accounts, and to seamlessly use other applications that use the same private key, to verify the tokens’ validity.

Description

JSON Web Tokens (JWT) are tokens generated by the server upon user authentication on a web application, and then sent to the client (usually a browser).

These tokens are then sent on every HTTP request, which allows the server to authenticate the user.

To ensure integrity, information contained in the token is signed by a private key, owned by the server. When the server gets the token back from the client, it just has to compare the signature sent by the client with the one it will generate with its private key. If the signatures are identical, the token is then valid.

JWT web tokens

Anatomy of a JWT token

According to RFC 7519, a JWT token is made of the following elements:

A “Header” section, containing the algorithm used for the signature, as well as the type of token (“JWT” in our case). The whole thing is encoded in Base64.
A “Payload” section, containing the token data, like the user name, date of token generation or expiry date. All of that is written in JSON and also encoded in Base64
A “Signature” section, that is the result of Header and Payload, concatenated and then encrypted with the private key.

Here is an example of a JWT token :

“Header” section :
{
“alg”: “HS256”,
“typ”: “JWT”
}

“Payload” section :
{
“iat”: 1480929282,
“exp”: 1480932868,
“name”: “Username”
}

A lot of information can be put into that second section (Payload), like the username, or the user’s rights on the application. The JWT specifications however clearly mention some keywords to be used, like “iat” (issued at = date and time of token generation, with a timestamp) or exp (expiry date).

As explained earlier, the Signature is generated by concatenating the Header and Payload, and by encrypting the whole thing. Which gives us, with a private key “L2VE5VpgChrVPmgh1hgL” :

Signature = HMACSHA256(
base64UrlEncode({“alg”: “HS256″,”typ”: “JWT”}) + “.” +
base64UrlEncode({“iat”: 1480929282,”exp”: 1480932868,”name”: “Username”}),
L2VE5VpgChrVPmgh1hgL
)

The resulting JWT token is :

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpYXQiOjE0ODA5MjkyODIsImV4cCI6MTQ4MDkzMjg2OCwibmFtZSI6IlVzZXJuYW1lIn0.gZeuWNbjO8kyEX92AjgX5oLy5qhu6YWTPr6vtYELZQ4

The different colors specify:
Header
Payload
Signature

Which signature should I use?

The JWT standard is quite flexible and allows developers to use no signature algorithm at all for the token. Of course, this kind of configuration is highly discouraged.
It is however possible to choose which algorithm to use. It is therefore possible to use HMAC SHA256 like in the examples exposed in this article, which is the recommended approach. It is also possible to use an asymmetric encryption configuration (with a private key owned by the server, and a public key used to sign the token). The problem with that second method is that anyone can sign a token (with the public key). To know more about that vulnerability, please refer to the following article :
https://auth0.com/blog/critical-vulnerabilities-in-json-web-token-libraries/

keys copy jwt

Use case

On the client-side, tokens can be stored in two different ways : stored in a cookie, or stored in the sessionStorage (or localStorage) of the browser. Each of these two methods have their own pros and cons, in terms of security and functionality.

Token stored in the sessionStorage or localStorage of the browser

If the token is stored that way, then it will have to be included in every request sent to the server, for instance with a header “Authorization: Bearer <token>”. That solution is really great for applications with a front-end written in JavaScript that communicates with the back-end through Ajax requests. Also, that solution natively protects the application against CSRF attacks considering that the token is unpredictable and cannot be retrieved by an attacker. Of course this last point is only valid if all sensitive requests use that JWT token. However, since the token must be made available to the JavaScript application, il will be exposed in case of XSS vulnerabilities and might be stolen.

Token stored in a cookie

When stored in the browser’s cookies, it is possible to set the “HttpOnly” flag (and “Secure”), to get protected against token theft in case of XSS attacks. The drawback of this implementation is that no CSRF protection can be expected from the token. Indeed, the token is automatically sent with the cookies (and therefore with any CSRF attack request).
Although that implementation remains “stateless”, it does not offer a seamless integration across different applications on different domains. Cookies are attached to a domain (or sub-domain) and cannot be used by another application hosted on a different domain.

Drawbacks of JWT tokens

Appart from pros and cons already detailed above, the JWT standard has its own issues :

  • If a user account needs to be blocked or deactivated, the application will have to wait for the token to expire in order for the lockout to be fully effective.
  • If a user needs to change their password (for instance in case of account hijacking) and if an authentication has been performed beforehand, then a token generated with the previous password will still be valid until expiry.
  • No “refresh” token is specified by the standard implementation. On expiry, the user will therefore have to re-authenticate.
  • It is not possible to destroy a token without breaching the “stateless” aspect of JWT tokens : Even if the token is deleted from the browser, it is still valid until expiry, so no real logout is possible.

To deal with these challenges, some JWT libraries add a layer above the standard specification and allow refresh token mechanisms as well as some features like forcing a user to re-authenticate, if need be.

Conclusion

JWT tokens are spreading over the web, mainly on applications relying on a (heavy) frontend and where users have to use a single account on different platforms. From a security point of view, JWT tokens have quite a good architecture but have some drawbacks that developers need to have in mind before integrating them into an application. It is very important to keep in mind that the security of the whole thing relies on the algorithm and private key used to sign tokens. Choosing a strong algorithm and key, and keeping the key in a safe place is therefore an important consideration.

Resources

To learn more on this topic, we encourage your to go through the following elements :
RFC 7519 (JWT tokens) : https://tools.ietf.org/html/rfc7519
Website with an overall JWT tokens presentation and references to libraries in several programming languages : https://jwt.io/

Author: Romain GARCIA