document.body.classList.toggle('menu-open', show); // Add 'menu-open' class to body.

JWT Security Part 3: Secret Key Management – the key to the kingdom

Jan Masters
Written by Jan Masters
November 28, 2024
Tags – ,

Welcome back, JWT aficionados! In our last post, we unravelled the mystery of JWT structure and basic implementation. This week, we’re going to talk about secret key management in JWTs.

Disclaimer: I am not a JavaScript developer; I hate JavaScript, so please use all of my code snippets as inspiration. You might find better examples on StackOverflow.

Why all the fuss about secret keys?

Remember how we said JWTs are like VIP passes to a fancy club? Well, the secret key is like the master key that creates all those VIP passes. If it falls into the wrong hands, suddenly everyone’s a VIP, and your exclusive club turns into a free-for-all party.

In JWT terms, the secret key is used to sign the token. This signature is what prevents users from tampering with the claims in the token. It’s the difference between “I’m an admin” and “I’m an admin, and the server agrees.”

The Do's and Don'ts of secret key management

DO: use strong, randomly-generated keys
Your secret key should be long, complex, and randomly generated. We’re talking “password so strong it makes your keyboard sweat” level of complexity.

“`javascript
const crypto = require(‘crypto’);
const secretKey = crypto.randomBytes(256).toString(‘hex’);

DON’T: hardcode your secret key
Hardcoding your secret key in your source code is like writing your ATM PIN on your debit card. It’s convenient, sure, but it’s also a disaster waiting to happen.

				
					```javascript
// Please don't do this
const secretKey = 'mySuper$ecretKey123!'; // No, just... no.
```
				
			

DO: use environment variables
Good practice for secret key management is to store your secret key in environment variables. It’s like keeping your house key in your pocket instead of taping it to your front door.

				
					```javascript
const secretKey = process.env.JWT_SECRET_KEY;
```
				
			

DON’T: use the same key everywhere
Using the same key for development, testing, and production is like using the same key for your house, car, and office. If one gets compromised, everything’s at risk.

DO: implement key rotation
Regularly changing your key is a good practice in secret key management. It’s like changing the locks on your doors periodically, just in case someone made a copy of your key without you knowing. Doing this will invalidate all current JWTs. This task should be done with planned downtime.

				
					```javascript
const rotateKey = () => {
const newKey = crypto.randomBytes(256).toString('hex');
// Update the key in your secure storage
// Gradually phase out tokens signed with the old key
};
```
				
			

DON’T: underestimate the importance of key length
A short key is the exact same as a short password – easy to guess and easier to crack. Aim for at least 256 bits of entropy in your key.

 

The nightmare scenario: key compromise

Imagine this: your secret key has been compromised. Maybe it was leaked in a GitHub repository, extracted from a server or source code, or it was simply brute-forced. What now?

  1. Immediate action: invalidate all existing tokens and force all users to log in again by updating the secret key for all unique applications or identity providers.
  2. Investigate: figure out how the key was compromised and plug that security hole.
  3. Learn: use this as a learning experience to improve your key management practices.

 

Secret key management advanced techniques: asymmetric signing

For the overachievers out there, consider using asymmetric signing in your secret key management. Instead of relying on a single key (as with symmetric encryption), asymmetric signing involves a public-private key pair. The private key is used to sign the token or message, while the public key is used to verify it. This technique is especially useful in distributed environments where you can share the public key safely for verification while keeping the private key secret.

Here’s an example of how you can generate a public-private key pair, sign a message using the private key, and verify it using the public key.

				
					```javascript
const { generateKeyPairSync, sign, verify } = require('crypto');

// Generate RSA key pair
const { privateKey, publicKey } = generateKeyPairSync('rsa', {
modulusLength: 4096, // Key size
publicKeyEncoding: { type: 'spki', format: 'pem' },
privateKeyEncoding: { type: 'pkcs8', format: 'pem' }
});

// Example message to sign
const data = "Important message";

// Sign the message using the private key
const signature = sign("sha256", Buffer.from(data), {
key: privateKey,
padding: require('crypto').constants.RSA_PKCS1_PSS_PADDING,
});

// Output the signature (base64 encoded for readability)
console.log("Signature:", signature.toString('base64'));

// Verify the signature using the public key
const isVerified = verify("sha256", Buffer.from(data), {
key: publicKey,
padding: require('crypto').constants.RSA_PKCS1_PSS_PADDING,
}, signature);

// Output verification result
console.log("Signature Verified:", isVerified);
```
				
			

This approach allows you to keep the signing key (private key) extra secure while distributing the verification key (public key) more freely.

 

Secret key management - in conclusion

Secret key management in JWT is no joke. It’s the linchpin of your entire JWT security strategy. Treat your secret keys with utmost care and paranoia.

Remember, a chain is only as strong as its weakest link. In the case of JWTs, your secret key management practices could very well be that link. So, keep it secret, keep it safe, and for the love of all things secure, don’t hardcode it!

Stay tuned for our next post, where we’ll dive into the murky waters of token revocation and refresh tokens. It’s like trying to un-invite someone from a party after you’ve already given them the address – tricky, awkward, but not impossible!

Like what you see? Share with a friend!

Jan Masters

This article is written by

Jan Masters

Cyber Security Engineer

In cyber security, there are no magic spells – just knowledge, experience, luck, and a touch of wizardry to turn challenges into solutions.

Cyber Security Engineer Jan – our resident Cyber Wizard (@FlyingPhishy on X, if you’re asking) – is a jack-of-all-trades consultant specialising in infrastructure, cloud, and R&D. He delivers high-quality, holistic penetration tests and drives innovation from within, ensuring our penetration testing services are modern and break the mould.