JWT Security Issues

The JWT standard and implementations exhibit a number of security related issues

6 minute read

JWT Security Issues

Security concerns

Signature Stripping

A malicious party can simply strip the signature, remove the encryption algorithm header so it seems no signature is provided, and then go ahead and alter the payload. If the client does not explicitly require a signature to validate against, this would be a valid, unsigned JWT.

Remedy:
Always require a signature by failing validation if no signature is present; this must be the very first thing in the processing of the request, so no further processing can take place.

Verification Algorithm Altering

At first glance this is the twin of Signature Stripping, but is sometimes more tricky to detect, depending on which JWT library is used.

Imagine a client that uses a private key to sign a payload, and a service that uses the public key to verify it; this can be very secure as long as the private key remains private. It usually looks something like this in code:

 
verificationKey = getVerificationKey()
verify(jwt_token, verificationKey)


Note in the above, that the verify function accepts whatever key is given, and that it will perform the verification according to the algorithm specified in the JWT header.
With an excepted asymmetric algorithm, such as RSA256, getVerificationKey would probably be designed to fetch the senders public key, which works perfectly, since the signature is created with the senders private key.

Consider what happens if an attacker have changed the verification algorithm to a symmetric algorithm, such as HMAC, which depends on a secret shared key. Since in a symmetric algorithm, the same key is used for both signing and verifying, the public key fetched in getVerificationKey will now successfully verify anything signed with that same publicly available key! Thus if we assume an asymmetric algorithm, but allow an symmetric algorithm (which, by the way the standard says we should), and attacker can simply change the algorithm header, then sign the payload with a publicly available key!

Remedy:
Always require a specific signature verification algorithm by failing validation if the wrong signature verification algorithm is present; this must be the very first thing in the processing of the request, so no further processing can take place. Carefully consider which key is used for signing; any public key can be used to re-sign an altered payload, even for asymmetrical algorithms, unless a private key is used for signing and the public is used for verification. In this scenario, it is fairly trivial to establish trust in the server, so the client can trust whatever is served; however, it may be less trivial to establish trust the other way, unless a client can provide a suitable public key for the server to use, in which case it is in fact a very useful scenario for communication between systems or parts of a system and can therefore be used to Establish Trust between Microservices

NOTE that the above attack description in no way is intended to imply that asymmetric keys, such as RSA, is superior to symmetric keys: In fact, symmetric keys can be agreed on using asymmetric keys, such as the often recommended Diffie-Hillman key agreement algorithm, which magically can use own private key together with other parties public key to generate the same shared secret key even though they sender and receiver have access to different keys, and can do this without actually exchanging any secrets! This makes it impossible for a third party to calculate the shared key.

Cross-Site Request Forgery (CSRF)

A malicious site can contain code that attempts to log in to a target site. If the user have recently logged into the target site and this has been saved as a cookie, the cookie will be sent to the target site and the log in will succeed, after which the malicious site have access to the target site as a logged in user. NOTE here that JWTs can be, and often are, stored as cookies, and will thus be sent along to the target site like any other authorization token would be.

Remedies:
Short lived JWTs can help, as can special headers set by the originating site.
Session cookies and tokens valid only for one request are other common mitigation techniques.
A system could and should be designed to expect the JWTs used for access to be sent as a BEARER token, and never even consider JWT in cookies for access control.
In terms of storing access control JWTs, the most powerful technique may be to never store the JWTs as cookies; if they need to be stored they can be stored in local storage. Unfortunately Cross-Site Scripting is still possible, see XSS. NOTE here that most servers returns access tokens, as well as other token such as those representing client side state, as cookies, instead of special headers.

Cross-Site Scripting (XSS)

TODO Write about XSS (cookies, including JWTs stored as cookies, can be protected using HttpOnly flag, prohibiting read by JavaScript)

Broken Elliptic Curve Diffie-Hillman Key Agreement

There is a raft of JWT libraries, and indeed the JWT specification itself, that did not verify that the public key of an Elliptic Curve shared key agreement algorithm are in fact on the curve, which could make them vulnerable to attack. For full details se this article . A very short summary follows here.

The name Elliptic Curve comes from the fact they are based on Weierstrass equations (ye olde y2 = ax3 + ax + b) which, when plotted on a graph, form an elliptic curve. These are seen to have a uniform distribution over finite fields, such as the P-256 field (the most widely used in cryptography). Without getting into the mathematical detail (see the article for a definition of P-256), the order of this field is a truly staggering number, and thus an attacker can't do much with this curve. However, if we can find another curve with a very small factor and use that to build malicious JWTs and keep repeating them, we can get information about the secret key's small orders, particularly if we use the recommended ECDH-ES key agreement algorithm, simply because it guarantees that the victim repeats it's own contribution to the agreed shared key, then deduce the actual secret static key (which is the S in ES - static). Bada-bing, bada-boom! We now have the secret key and can use it to sign whatever we want!

Remedy:
Make sure the library verifies that the result of the scalar multiplication is in fact of the correct curve, or in simpler terms, that the public key used in the shared key agreement is on the curve. In some cases the language itself will protect against this attack, such as the latest version of Node.js and later Java versions. This attack is surprisingly easy to perform, see the demonstration .

Update Most, if not all, libraries has since been updated to protect against this attack.

Stateless Session using JWT

There is a host of concerns when using JWT for Stateless Sessions, and I will address them in turn.

comments powered by Disqus