We’ve been running across a lot of modern web applications lately that have implemented JSON Web Tokens (also known as JWTs) for session tracking. JWTs are an open, industry standard designed to securely transmit information between two parties as a cryptographically-signed, JSON object. While the JWT specification is designed generically to account for a variety of uses, the most common implementation for it in web applications is to handle authorization. Modern web applications leverage JWTs, as opposed to proprietary session identifiers, so that user’s sessions can be tracked across platforms and domains, such as through Single Sign On (SSO) or for multi-faceted application architectures.
How Do JWTs Work?
Before we discuss vulnerabilities specific to JWTs, it’s important to understand how they are supposed to work normally. At the core, a JWT is simply a Base64 encoded string with three parts separated by periods: a header, a payload, and a signature. The header will usually have two parts that describe the tokens type (which is JWT obviously) and the signing algorithm used (which can be one of a set of canned options). The payload consists of a set of id:value pairs that are the meat of the token. Some of these values are built-in and describe the token issuance datetime and expiration datetime, while others are custom, application-specific depending on the information needing to be tracked for a session. Finally, the signature is a cryptographic hash of the header, payload, and a secret using the specified algorithm from the header and attached to the token. This is what protects the integrity of the rest of the JWT and prevents unauthorized modification (foreshadowing, perhaps?). In summary, the final product for a full JWT looks something like this:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjMwibRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.IIdrKuF1Wh4mU5fxxtIegrfDc3xDwPYAujfbwA sample JWT – don’t bother trying to reverse it, it’s not really valid
Now let’s cover some of the most common issues that plague applications leveraging JWTs. We’ll keep these at a high level, since most of these problems have implementation-specific causes/fixes and can very in exploitability.
No manual blacklisting process
By far, the most common thing I have reported relating to JWT implementations are the failure to use a manual blacklisting process for them. JWTs have no concept of valid/invalid outside of their issuance and expiration times. This means that when a user clicks “Logout” within your application, you can remove the cookie containing the JWT from their browser but the JWT string itself is still valid and it can be used until the expiration time has passed. So an attacker that is able to obtain a JWT through some other means could continue to use it, even after a user has logged out, potentially allowing them to impersonate or replay that user’s requests and gain unauthorized access to data.
To address this issue, application developers have to implement a manual blacklisting process to track invalid JWTs until they expire and check all subsequent requests containing a JWT against that list. If a token is in the list, that means it’s invalid and the request should not be processed. This list can be flushed/cycled based on expiration times on the JWTs contained.
JWT Information Disclosure
Another common issue with JWTs is related to sensitive information being disclosed through them. The information/claims that are included as part of a JWTs payload are ultimately just base64 encoded, from a confidentiality standpoint. So while they should be signed to prevent modification, anyone can view the payload data of a JWT. Application developers should avoid storing any information here that should not be public, such as usernames, username formats, passwords, security roles, etc.
Improper Client-Side Storage
Weak Secret Used for Signing
As you may be able to guess, a large part of the security for JWTs is baked into the fact that the header/payload are subsequently signed to prevent modification. If an attacker can modify a payload to change a user ID and sign the modified JWT with a legitimate hash, for example, they’d likely be able to impersonate another user. The Secret that is used in calculating the signature is what prevents this. However, this value can be targeted with password attacks, so it must be set to a strong, random value. Some frameworks will leverage the word “secret” as the default (it does have to be a minimum of 6 characters), so if this isn’t changed any JWT could be freely modified and resigned, appearing legitimate.
The None Algorithm
Finally, this issue is one I have yet to see in the wild. But an application developer can actually set the “Alg” value in the JWT header to “None”. This means that the application won’t bother to calculate/check a signature associated with the JWT and simply accept base64 encoded tokens with 2 fields: the header and the payload. Without requiring a signature, the payload can be freely modified by an attacker, leading to all the bad things mentioned that are associated with Weak Secrets.
Hopefully these things give you some insight as to what to watch out for if you are creating or attacking applications that are leveraging JWT implementations. OWASP has a lot more information on each of these kinds of attacks, including some sample code for avoiding these issues in applications here. As always, if you have a questions/comments or want to figure out if your web application is vulnerable to some of these attacks discussed, please reach out!