JWT handling best practices

JWT, short for JSON web tokens, is a popular method for managing user authorization and authentication in web applications. It is designed for allowing parties to transmit information securely. JWT is a good choice when implementing custom security mechanisms in applications because, in addition to the security, almost every popular technology provides support for JWTs.
JWT structure
A properly generated JWT consists of three parts: Header, Payload, and Signature. These three parts are encoded separately using Base64url, and each is concatenated to the other using periods to create the token.
This is what an encoded JWT string looks like:
eyJ0eXAiOiJKV1QiLCJhbGciOiJub25lIn0.eyJ1c2VybmFtZSI6ImFzZCIsImFkbWluIjp0cnVlfQ.HGcvua9PvrK-xq1_ed4GoCrXYrKkGgGKqvpXk94fKoo
This resulting token can easily be passed into HTTP and HTML.
Header
The JWT Header contains information about the type of the token and about the cryptographic algorithm used to generate the signature. An example is shown below:
{ “alg”: “HS256”, “typ”: “JWT” } |
The “typ” field defines the token type. If it exists, it must be set to a registered IANA Media Type. The “alg” field represents the message authentication code algorithm. This algorithm can be set freely, but it’s important to note that some of the supported algorithms are not secure. In this example, the chosen algorithm is HMAC with SHA-256.
Payload
The JWT Payload contains a set of claims which can be standard or custom properties. The seven standard fields are defined in the JWT specification. For example:
- iss – Issuer: describes the issuer
- aud – Audience: the actual recipients
- exp – Expiration Time: defines the valid time interval for accepting the JWT
- sub – Subject: JWT subject
Depending on the functionality of the token, custom fields are also an option in the Payload.
{ “username”: “john”, “admin”: “false” “exp”: 1548632574 } |
In this example, username and admin are custom fields, while exp is a standard field.
Signature
The Signature is responsible for secure validation of the token. It uses Base64url Encoding to convert the Header and Payload, and the results are concatenated by a period separator. After the procedure, the completed string runs through the defined algorithm from the Header, which is “HS256” in the previous example.
The attack in practice:
The following Ruby code is a simple file opener/reader.
HMACSHA256( base64UrlEncode(header) + “.” + base64UrlEncode(payload), secret) |
How does JWT work?
During the authentication process, after the user has successfully logged in, the returned JWT will be stored locally. Session storage is used for this in most cases, but cookies are also a viable option. But it’s not just the server which can generate a JWT – if the requirements meet the appropriate standards, the client is also allowed to generate its own JWT. The JWT must contain a pre-shared secret which needs to be passed towards an OAuth compatible service. The server then checks the generated JWT and if it is valid, an access token will be generated by the server and sent back to the client. If the client would like to communicate with the server through protected routes, the JWT needs to be forwarded because these routes check for valid tokens in the Authorization header. On the client side, the user agent is responsible for attaching the JWT in the Authorization header using the Bearer schema. If the JWT is in its place, the client is able to access the protected data.
When and why use JWT
Using JWT tokens by default is not a good idea. Due to its complexity, there is a higher possibility of security mistakes occurring. The amount of data being carried is also increased. Each request contains a ton of overhead. One pro of using JWT is that the server does not need to use a database to store information about the client, because it is already stored in the JWT. So overall, for implementing API authentication and server-to-server authorization, JWT can be a very useful technology depending on the intensity of the communication between the server and client.
Security concerns (basic misconfigurations, most common security pitfalls and mistakes)
Because JWT is an authentication and authorization related technology, the impact of failures and security vulnerabilities can be quite critical. The IT community has collected a large number of common security pitfalls for JWT. JWT implementation mistakes could lead to very severe security issues. The most common ones are below:
Improper JWT Validation
There are many ways in which JWT validation can be broken during implementation. The most typical are listed below:
Missing validation of the Signature:
The JWT payload is decoded despite the JWT token being invalid because the signature was never validated.
Missing validation because the None alg is provided:
It is possible to create a token by setting the JWT algorithm to “None”. The server has to check that the “None” algorithm was implemented during the JWT creation process. Regardless, each JWT with the “None” value in the “alg” field will be managed as a valid token.
Changing from RS256 to HS256:
The RS256 uses different keys for signing and validation – that is, a public key and a private key. HS256, on the other hand, uses the same key for the same process. This means an attacker could create the token by setting the signing algorithm to HS256 instead of RS256, which results in the API blindly verifying the token using the HS256 algorithm, using the public key as a secret key.
Safe secret key creation
The secret key must be a complex character combination. This ensures the key will be unpredictable and safe. Developers often use copy-paste coding, and while this can be effective, its downside is that developers can be inattentive and forget to reimplement the necessary functions and reset the original parameters. This can occur during the implementation of JWT when the developers use a template code and leave a static simple string as the secret key instead of generating a random one. A brute force attack can very effectively exploit this vulnerability.
Setting JWT as a session cookie
There are situations where the JWT needs to be revoked, such as the log out process. The server should invalidate the token, but it is impossible because the JWT is stored on the client side, and the server does not know which JWT should be revoked. Storing the linked user token pairs in a database on the server is also an option, but this is not what the JWT is designed for.
Accidentally revealing the secret key
JWTs are created by private secret keys. The key is a critical factor in the process and it needs to be hidden from the public as much as possible. Sadly, developers sometimes make mistakes and leak the secret key by storing it on the client side javascript code or in publicly accessible files. If this happens, an attacker can break the signature verification with ease. From this point, the attacker can sign any payloads with the secret key and trick the signature validation on the server side.
Summary
A correctly implemented JWT can help with authorization, authentication, and transferring data between parties, but it is not always the best or most effective solution. Using JWT is a choice between security and performance, and deciding between them depends on the purpose. If you need to use JWT, then make sure the implementation is properly realized to avoid the possible security pitfalls and protect the application as much as possible.
Share this post on social media!
Related Articles
Ruby needs security
Every year, Ruby is becoming more and more popular thanks to its elegance, simplicity, and readability. Security, however, is an issue we can’t afford to neglect.
Python best practices and common issues
Python is a high-level, flexible programming language that offers some great features. To be as effective as possible, it is important to possess the knowledge to make the most out of coding with Python.
5 Steps your security program should include
For most companies, security is considered a side quest, which is partly related to the daily processes. In reality, security ought to be a strong foundation of any organization. To ensure the defense of the enterprise, the relevant teams need strong security knowledge and abilities.
