<img height="1" width="1" src="https://www.facebook.com/tr?id=1879927395628828&amp;ev=PageView &amp;noscript=1">

Easy to Implement JSON Web Token (JWT) Bugs: How to Avoid and Mitigate Them

 Feb 19, 2018 7:15:00 AM |    Brandon Bachman

Stop.jpg

As I was doing some training the other day I came across some rather interesting bugs in a commonly used technology: JSON Web Tokens (JWT). Although JWT itself is fundamentally secure, some implementations are not. This post focuses on a specific type of bug related to certain implementations of the technology.

A bit of background on JWT; for those of you who aren’t familiar with JWT, it’s an open standard (RFC 7519) that defines a method of securely transmitting information between parties as a JSON object. You might find JWT in use for authentication mechanisms such as Single Sign-On and session management, or even for information exchange (utilizing JWTs ability to validate both the sender and content). I realize this short explanation doesn’t cover the breadth of what JWT has to offer, but it should provide sufficient context to understand the bug.

The anatomy of the tokens is as follows: Three values each separated by a dot ( xxxx.yyyy.zzzz) consisting of the header, payload, and signature. The header contains information about the algorithm and type, which looks like the following: { "alg": "HS256",  "typ": "JWT"}. In the actual token this value, along with the payload and signature, will be base64 encoded. The payload contains claims, which are statements about an entity, typically the user, along with some additional metadata. The signature is generated by using the algorithm specified in the header along with the secret key to sign the the encoded output of the concatenation of the header and payload. With all three parts the complete base64 encoded JWT looks something like the following:

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJsb2dnZWRJbkFzIjoiYWRtaW4iLCJpYXQiOjE0MjI3Nzk2Mzh9.gzSraSYS8EXBxLN_oWnFSRgCzcmJmMjLiuyu5CSpyHI

Bugs.jpg

 Now, for the bugs…

The whole point of JWT is to securely transmit information, right? So the server needs to verify the JWT using the signature to ensure that it the data inside hasn’t been modified. So what happens when the server tries to verify a token? It has the signature, but the server needs to know which algorithm was used to generate it (otherwise it’ll be a bit difficult to decode). Thats easy! Just take the alg field from the header. No problem, right? But wait, the token isn’t validated yet, which means the header field isn’t validated. This is a bit of a catch-22. To verify the header you need to verify the signature but to verify the signature you need to verify the header. Without external verification, this leaves the ‘alg’ value exposed to attackers that wish to modify it (meaning the server isn’t going to validate it).

So why is this a big issue?

Well, because JWT supports multiple signature methods that can be used to ensure the integrity of the token: RSA, Elliptic Curve, HMAC, and None (varies based on implementation, but none and HMAC are required). Since users have the ability to specify which algorithm the server uses to verify the signature (without validation), they can select the none algorithm. Some libraries will treat tokens signed with the none algorithm as a valid token with a verified signature. Thus, any implementation exposed to this bug allow users to create their own signed tokens with any payload, allowing arbitrary access depending on the system. All an attacker would have to do is supply a JWT with the ‘none’ algorithm and an empty signature.

What about the other choices for the ‘alg’ field? Is there more that can be done with it? Certainly.

 

Consider a JWT library with an API that contains:

 

decode(string token, string key)

 

If using HMAC, key will be the server’s secret key (HMAC is symmetric). Such as:

 

decode(token, HMACSecret)

 

If using asymmetric, key will be the public key that’s used to verify the token. These tokens are signed with the RSA secret key. Such as:


decode(token, RSApublickey)

Lets focus on the second implementation, when the server is expecting an RSA256 public key to verify the token. Consider what happens when an HMAC token is supplied to the function instead of a token signed with the RSA private key. If an HMAC token is submitted, the function will still try and verify the signature against the public key, but in this instance its verifying the HMAC token against the public key. An attacker can abuse this by collecting the RSA Public key used to verify signatures and then use it to sign their own HMAC token. If this token is supplied along with HS256 as the ‘alg’ field, the server will verify the HMAC (symmetric) token against the same key used to generate it (the RSApublickey) which will pass. This essentially gives any old Joe Schmo with access to the public key the ability to forge a token that the server will accept!

JSON.jpg

Now for some suggestions to anyone using or looking to use JWT.

The good news is, remediating both of the vulnerabilities mentioned above is not complex in most cases, and you may just be able to choose an existing library without them. Anyone using the following vulnerable libraries should update to an unaffected version*:

 

Python - Pyjwt - prior to version 1.0.1

Node.js - jsonwebtoken - prior to version 4.2.2

Javascript - jsrsasign - prior to version 3.2.0

PHP - firebase/php-jwt - prior to version 2.0.0

PHP - lcobucci/jwt - prior to version 3.0.0

 

*: Note that not every affected library is posted here, only the ones linked through the JWT.io website.

The JWT.io website includes a plethora of JWT libraries that span a wide array of languages and technologies including Python, Javascript, Node.js, Java, .NET, and more. The website’s catalogue of libraries also includes minimum versions for libraries known to be vulnerable to the bugs discussed in this post, so it would be a good idea to consider making use of them before attempting to create a custom implementation. My advice is, use what you know to be reliable, secure, and effective. All it takes is a bit of research, no need to reinvent the wheel.

For those of you interested in preventing these bugs in your own implementation, or just the curious, consider these suggestions:

Avoiding the none algorithm altogether is a safe bet if the implementation is not expecting unsecured JWTs. There are situations in which unsecured JWTs would be useful (see: JSON Web Signatures) but in general if a secret key was provided, all tokens containing the ‘none’ algorithm should be rejected. This simple fix is good enough to prevent users from submitting valid empty tokens, but more is needed to resolve the problem with algorithm selection.

Allowing attackers to provide the algorithm value is a problem. This means avoid looking at the ‘alg’ field in the header altogether, the data is not to be trusted! As mentioned above in this post, the header can’t be verified without the signature, so using it for verification of anything can get you into trouble. Unvalidated user input is a rather common issue as its difficult for developers to consider every possible outcome in a system with complex functionality. In this case there is a selection of algorithms and differing forms of cryptography (asymmetrical and symmetrical) which is rather easy to implement insecurely if all possible combinations of token submittal are not considered. Thusly, whitelisting algorithms that are ‘safe for use’ is a good idea as it should give a simple filter: only what you need. You won’t have to worry about considering how differing forms of cryptography mingle because the server will only accept what it expects. A whitelist like this should ensure that tokens with a different signature type than the supported algorithm are rejected. Another useful addition to stump attackers attempting to switch the ‘alg’ value in the JWT header would be an algorithm field in the verification function (just like the three decode functions above). If the signature type doesn’t match the specified algorithm in the decode function then verification will fail!

Considerations would have to be made if a server implementation is required to support more than one algorithm, but it’s possible to do securely. In this case, a separate key would need to be used for each algorithm. Conveniently enough, JWTs come along with a ‘kid’ field which can be used by the server to match the key to the corresponding algorithm and (secret/public)key. This effectively negates attackers ability to control how a (secret/public)key is used for verification as the server can simply use the key id to lookup the (secret/public)key and corresponding algorithm.

 

As shown in this post, even if you have a fundamentally secure technology like JWT, often times it's easy to implement it in an insecure way.

 

img-banner-cta.jpg 

Topics: Secure Development, Vulnerability Remediation

Want more of the AsTech Blog? You got it.
Blog subscribers get email updates twice a week.

Comments