Hacking JSON Web Tokens | Websecurify Blog (2024)

JWT, or JSON Web Tokens, is the defacto standard in modern web authentication. It is used literally everywhere: from sessions to token-based authentication in OAuth, to custom authentication of all shapes and forms. There is actually a pretty good reason for this wide adoption and that is, for the most part, security and resilience. However, just like any technology, JWT is not immune to hacking. In this post I will show you exactly how this can be done using our painless online security toolkit - of course.

Before we begin, I need to mention that the attack technique that you will experience here comes from Tim McLean's research, originally published on the auth0 blog over here. It is an excellent read that you should dig into as soon as you finish with this post. I will put another link at the bottom of this article for easy access and follow up.

Our journey begins with a piece of vulnerable code of an example service (use your imagination) written in node with ExpressJS (the latest version at the time of writing).

var fs = require('fs')var express = require('express')var jwtSimple = require('jwt-simple')var app = express()app.get('/', function (req, res) { res.set('Content-Type', 'text/plain') res.send('API')})app.get('/key.pem', function (req, res) { res.set('Content-Type', 'text/plain') res.send(fs.readFileSync('key.pem'))})app.get('/get_token', function (req, res) { res.set('Content-Type', 'text/plain') res.send( jwtSimple.encode( { message: 'Hello world!' }, fs.readFileSync('key'), 'RS256' ) )})app.get('/verify_token', function (req, res) { var token = req.query.token var decoded try { var decoded = jwtSimple.decode(token, fs.readFileSync('key.pem')) res.set('Content-Type', 'text/plain') res.send(decoded) } catch (e) { console.log(e) res.set('Content-Type', 'text/plain') res.send('Error') }})app.listen(8080, function () { console.log('App listening on port 8080!')})

This so-called API does two things. First, it generates a signed JWT token with a static message via a call to /get_token endpoint. For the signature we use a proper public and private key pair. The JWT token can be validated and the message payload decoded using the /verify_token endpoint. If the JWT token is not tampered, the verification endpoint will return the payload to the user. Given that the private key is kept private and the public key is, well public and served by /key.pem endpoint, how can we exploit this service so that we can forge our own tokens?

As usual, examples in security-related blog posts and tutorials are conveniently easy to hack. The service that we face contains a subtle bug. Can you see it?

Let's start the service and see how it works. Create index.js with the code above and package.json with the following list of dependencies.

{ "dependencies": { "express": "^4.14.1", "jwt-simple": "^0.5.1" }}

We also need to install the dependencies and setup our public/private key pair for use for this service. As per the service code above, the private key is a file called key while the public key is in the PEM format and called key.pem. To setup both files we need to execute the following commands:

$ ssh-keygen -t rsa -N '' -f key$ ssh-keygen -f key.pub -m PEM -e > key.pem

Once the service is up and running. Let's use Rest to see how it works.

Hacking JSON Web Tokens | Websecurify Blog (1)

The setup is straightforward. Once we fire the request we get a token. In order to validate the token, open Rest in another tab with a token URI query parameter configured like the screenshot bellow.

Hacking JSON Web Tokens | Websecurify Blog (2)

As expected when we verify the token, we get the static message back, which is Hello world! - so possitive. If we are to change the token with our own forged token, we will get an error response as illustrated by the following screenshot.

Hacking JSON Web Tokens | Websecurify Blog (3)

Notice how we conveniently turned off the original token parameter without deleting it and added a new token with the new JWT token value. This is what happens when you have proper tools.

Up to this point we have a service that employs JWT which we proved that is not susceptible to tampering by conveniently using the HMAC signing algorithm to forge tokens, which should not work - or should it. Most of these types of articles are like magic - not very impressive once you know the trick.

To make me look needlessly 31337, let's dissect trivial code see how tokens are generated by the jwt-simple dependency. The encode method has the following signature:

/** * Encode jwt * * @param {Object} payload * @param {String} key * @param {String} algorithm * @param {Object} options * @return {String} token * @api public */jwt.encode = function jwt_encode(payload, key, algorithm, options) {

Further inside the code we learn that the signing algorithm defaults to HS256 if no algorithm parameter is provided.

// Check algorithm, default is HS256if (!algorithm) { algorithm = 'HS256'}

This is why we specifically tell the encode function in our API to use RS256 which, instead of using a symmetric secret, will utilise our key pair generated earlier in the article - and everyone knows that asymmetric cryptography is better than symmetric cryptography.

Next step is to look into the decode function, which happens to have the following signature:

/** * Decode jwt * * @param {Object} token * @param {String} key * @param {Boolean} noVerify * @param {String} algorithm * @return {Object} payload * @api public */jwt.decode = function jwt_decode(token, key, noVerify, algorithm) {

This function signature is slightly different from the previous one due to the noVerify parameter. The function still expects an algorithm to be supplied but perhaps its value also defaults to something like the previous function. Let's have a look.

var signingMethod = algorithmMap[algorithm || header.alg]var signingType = typeMap[algorithm || header.alg]if (!signingMethod || !signingType) { throw new Error('Algorithm not supported')}

So it looks like that unlike the encode method which uses default algorithm set to HS256, the decode method defaults to the algorithm specified in the JWT header, which we have complete control of - classic arbitrage. This means that if we do not happen to explicitly specify the algorithm when we call the decode function, we will end up verifying the token with the algorithm specified by the attacker. As it turns out our service does exactly this - I told you it is way too convenient.

So up to this point all we know is that the attacker with the hoodie controls the signing algorithm that is used to verify the signature of the token. But earlier on we forged our own token using the HMAC algorithm and that did not work.

Hacking JSON Web Tokens | Websecurify Blog (4)

Clearly the header contains HS256 which the jwt-simple library will use because the developer did not provide their own defaults. However, notice that when we generated the token using HS256 we also used arbitrary signing key with the value secret. Of course this is not going to work because the decode method does not have the same key and there is no way we can force-feed it.

However, if the verification algorithm is HS256 than what is the key? Clearly, in our example the key is the public key as you would expect if we are using the RS256: private key signs - public key verifies. Aha! So in fact, when we forge our own token with HS256, the secret key for the verification method is nothing else but the public key which as you can imagine is - public!

Equipped with this awesome information let's try again but this time when we forge the token we will use the public key instead of the arbitrary string we used earlier. We can retrieve the public key from /key.pem.

Hacking JSON Web Tokens | Websecurify Blog (5)

If you just copy and paste the key into the token parameter that has the JWT generator, it may not work. The reason for this is because both strings need to be exact in order for the HS256 to work. Spaces, line-endings and all kinds of other characters that you may not visually see, or be able to copy with CTRL+C, are required for this to work. It needs to be 1-to-1. This is where we will use our file retriever to make this perfect.

Hacking JSON Web Tokens | Websecurify Blog (6)

Voila! Magic happened. We managed to forge our own token using a simple trick on a conveniently vulnerable API.

I hope you have noticed that I was a bit cynical about how conveniently vulnerable this service was. It is easy, indeed. However, these types of scenarios are far too common to call them impractical bugs. In fact, larger code-base tend to complicate the nature of all of this so much that it becomes very difficult to spot subtle bugs.

As promised, here is the link to Tim McLean's article. It is a good read which I think will give you further insights of how JWT actually works if all of this is new to you.

I have some final words for people who are saying that JWT generally sucks and all that. It doesn't! It is actually very clever what it does and it does what it is supposed to do very well. However, crypto bugs are not always obvious even when you deal with them at such high level.

Check us out on twitter. We like to post pictures of robots in ASCII from time to time.

Hacking JSON Web Tokens | Websecurify Blog (2024)

FAQs

Can I hack a JWT token? ›

It is used literally everywhere: from sessions to token-based authentication in OAuth, to custom authentication of all shapes and forms. There is actually a pretty good reason for this wide adoption and that is, for the most part, security and resilience. However, just like any technology, JWT is not immune to hacking.

How to decrypt JSON Web Token? ›

JWT Decoder
  1. *First, remember that JWTs are tokens that are often used as the credentials for SSO applications. ...
  2. Grab a JWT (RFC 7519) you want to decode. ...
  3. Paste the JWT into the first text box.
  4. Press the Decode button.
  5. Read the decoded outputs for the header and payload!

Is it possible to steal a JWT token? ›

If someone steals the token, they can impersonate your user. We usually store JWTs in cookies, which makes us vulnerable to XSS and CSRF attacks. There are three important things to do: Use HTTPS - end-to-end TLS prevents someone intercepting or sniffing the requests on the wire and stealing the token.

Why are JSON Web tokens not safe? ›

JWT is the most common authentication token. It enables the use of many web applications and APIs while being authenticated, without frequently signing in. However, there are many threats that come with JWTs because they are neither encrypted nor implemented with security in mind.

Can JWT tokens be hijacked? ›

JWT tokens provide secure access to an authenticated user, and attackers are always looking for ways to steal these tokens and quickly gain access by impersonating a consumer.

Can you spoof a JWT token? ›

Tampering and Forgery: If an attacker can modify the contents of a JWT (e.g., by spoofing, changing the claims or the signature), they can potentially gain unauthorized access to resources or escalate their privileges.

Can we decode a JWT token without a secret key? ›

With all this in mind, remember that anyone can decode the information contained in a JWT without knowing the private keys. For this reason, you should never put secret information like passwords or cryptographic keys in a JWT.

What is the JWT secret key? ›

A user provides their credentials (e.g., username and password) and sends them to the server. The server validates the credentials. If they are correct, the server generates a JWT containing the user's information (in a claim) and signs it with a secret key. The server sends the JWT back to the user.

Can JSON Web tokens be encrypted? ›

Security of JWTs

The information contained within the JSON object can be verified and trusted because it is digitally signed. Although JWTs can also be encrypted to provide secrecy between parties, Auth0-issued JWTs are JSON Web Signatures (JWS), meaning they are signed rather than encrypted.

Can anyone read a JWT token? ›

Remember, JWTs can be seen by anyone who intercepts the token because it's serialized, not encrypted.

How do you tell if a token is a JWT? ›

A JSON Web Token (JWT) includes three sections with a . (dot) delimiter between them. The key ID, kid , and the RSA algorithm, alg , that Amazon Cognito used to sign the token. Amazon Cognito signs tokens with an alg of RS256 .

Can you destroy a JWT token? ›

By definition, once generated, a jwt token is valid until expired. You can “logout” and remove the token from browser storage, but the token is still valid.

Why are JWTs bad? ›

So why is JWT dangerous for user authentication? The biggest problem with JWT is the token revoke problem. Since it continues to work until it expires, the server has no easy way to revoke it. Below are some use cases that'd make this dangerous.

What is the alternative to JSON web token? ›

OAuth2, Passport, Spring Security, JavaScript, and Git are the most popular alternatives and competitors to JSON Web Token.

Where are JSON Web tokens stored? ›

To keep them secure, you should always store JWTs inside an httpOnly cookie. This is a special kind of cookie that's only sent in HTTP requests to the server. It's never accessible (both for reading or writing) from JavaScript running in the browser.

Is it possible to forge a JWT token? ›

By targeting files with predictable content, it's possible to forge a valid JWT. For instance, the /proc/sys/kernel/randomize_va_space file in Linux systems, known to contain the value 2, can be used in the kid parameter with 2 as the symmetric password for JWT generation.

Can you decode a JWT without secret? ›

With all this in mind, remember that anyone can decode the information contained in a JWT without knowing the private keys. For this reason, you should never put secret information like passwords or cryptographic keys in a JWT.

Can you tamper a JWT token? ›

There are multiple options for JWT tampering. Some web applications do not validate the signature, or don't use it at all. That means an attacker can modify the contents at will, insert all kind of nasty payloads (XSS, SQLi), ignore the expiration time by using an arbitrary value for the timestamp, and so on.

Is a JWT token secret? ›

A JWT consists of a header, payload, and a digital signature. The header and payload are base64url-encoded and are used with a secret key to generate a digital signature. The server sends the JWT to the browser after user authentication.

Top Articles
Five dimensions of impact for actionable result | Sopact Perspective
How to Manage Sensitive Log Data
How To Fix Epson Printer Error Code 0x9e
The Largest Banks - ​​How to Transfer Money With Only Card Number and CVV (2024)
Dew Acuity
Wmu Course Offerings
Free VIN Decoder Online | Decode any VIN
Www Craigslist Louisville
Wmlink/Sspr
True Statement About A Crown Dependency Crossword
Osrs Blessed Axe
10 Great Things You Might Know Troy McClure From | Topless Robot
Hope Swinimer Net Worth
Oc Craiglsit
Craigslist Pets Longview Tx
Wisconsin Women's Volleyball Team Leaked Pictures
What Happened To Maxwell Laughlin
How do you like playing as an antagonist? - Goonstation Forums
Nj State Police Private Detective Unit
Abby's Caribbean Cafe
Juicy Deal D-Art
Pokemon Unbound Shiny Stone Location
Gina Wilson All Things Algebra Unit 2 Homework 8
Doublelist Paducah Ky
12 Facts About John J. McCloy: The 20th Century’s Most Powerful American?
Meridian Owners Forum
Used Patio Furniture - Craigslist
Scott Surratt Salary
Worthington Industries Red Jacket
1964 Impala For Sale Craigslist
lol Did he score on me ?
1400 Kg To Lb
Barrage Enhancement Lost Ark
John F Slater Funeral Home Brentwood
Louisville Volleyball Team Leaks
Studentvue Columbia Heights
Wlds Obits
sacramento for sale by owner "boats" - craigslist
התחבר/י או הירשם/הירשמי כדי לראות.
Craigslist Com Panama City Fl
Lacy Soto Mechanic
Best GoMovies Alternatives
Lamp Repair Kansas City Mo
Tattoo Shops In Ocean City Nj
Searsport Maine Tide Chart
Caesars Rewards Loyalty Program Review [Previously Total Rewards]
Keci News
Sapphire Pine Grove
Strange World Showtimes Near Marcus La Crosse Cinema
300+ Unique Hair Salon Names 2024
Runescape Death Guard
Varsity Competition Results 2022
Latest Posts
Article information

Author: Carmelo Roob

Last Updated:

Views: 5997

Rating: 4.4 / 5 (45 voted)

Reviews: 84% of readers found this page helpful

Author information

Name: Carmelo Roob

Birthday: 1995-01-09

Address: Apt. 915 481 Sipes Cliff, New Gonzalobury, CO 80176

Phone: +6773780339780

Job: Sales Executive

Hobby: Gaming, Jogging, Rugby, Video gaming, Handball, Ice skating, Web surfing

Introduction: My name is Carmelo Roob, I am a modern, handsome, delightful, comfortable, attractive, vast, good person who loves writing and wants to share my knowledge and understanding with you.