~ 4 min read
How to Avoid JWT Security Mistakes in Node.js
What is JSON Web Token (JWT) and how can you use it securely in your Node.js applications? In this post, I’ll cover the basics of JWT and share best practices to avoid common security mistakes.
So, what is JWT?
JWT is an open standard for securely transmitting information between parties as a JSON object. It’s commonly used for authorization - allowing a client to access protected resources on a server.
The key aspects of JWTs are:
- Verification: JWTs are signed, which means the server can verify the token’s integrity. This prevents third-parties from tampering with the token’s content.
- Encryption: JWTs can be encrypted, which means the payload is hidden from everyone except the intended recipient. This is important aspect because mostly for web developers, JWTs are stored on the client-side and are easily accessible.
- Stateless: JWTs are self-contained, so the server doesn’t need to separately store session information.
In terms of structure and common flow of JWTs, they consist of three parts: a header, a payload, and a signature. When a user authenticates through a login page or an API request, the server creates a JWT, signs it, and sends it back to the client. The client then includes this token in the Authorization header for subsequent requests.
Using jsonwebtoken in Node.js
In Node.js, we can use the popular jsonwebtoken
library to work with JWTs. Here’s a simple example:
const jwt = require('jsonwebtoken');
const SECRET = 'secret1234';
// Generate a JWT
const payload = { userId: 123 };
const token = jwt.sign(payload, SECRET, { expiresIn: '1h' });
// Verify a JWT
const decoded = jwt.verify(token, SECRET);
console.log(decoded.userId);
The sign()
method is used to create a new JWT, while the verify()
method is used to validate an existing token.
Avoiding JWT Security Mistakes
While JWTs can be a secure way to handle authentication and authorization, it’s easy to make mistakes that compromise your application’s security. Here are some common pitfalls to avoid:
1. Use a strong secret key
âś… Do: Use a long, random, and secret string as your signing secret. Pass the secret in a secure fashion.
❌ Don’t: Use a weak or predictable secret key, or hardcode it in your application.
2. Validate the JWT signature
✅ Do: Always verify the JWT signature using the correct secret key before trusting the token’s contents.
❌ Don’t: Decode the JWT token without verifying the signature, as this allows an attacker to forge a valid-looking token.
3. Implement token revocation
âś… Do: Maintain a denylist or invalidate tokens when a user logs out or their permissions change.
❌ Don’t: Rely solely on short token expiration times to manage access, as this can lead to security vulnerabilities.
4. Use secure cryptographic algorithms
âś… Do: Use recommended algorithms like RS256 (RSA Signature with SHA-256) or HS256 (HMAC with SHA-256).
❌ Don’t: Use insecure algorithms like HS384, HS512, or any of the deprecated “HS” algorithms.
5. Validate token claims
âś… Do: Verify claims like iss
(issuer), sub
(subject), aud
(audience), and exp
(expiration) to ensure the token is valid.
❌ Don’t: Blindly trust the information in the JWT payload without validation.
A Common JWT Security Mistake in Node.js
One common misconception about JWTs I see in Node.js application code is that developers confuse or completely unaware of the difference between decoding and verifying a token. Here’s an example of insecure code that only decodes a token without verifying its signature:
const jwt = require('jsonwebtoken');
const app = require('express')();
const SECRET = 'secret';
app.get('/api/users/:id', (req, res) => {
const token = req.headers.authorization.split(' ')[1];
// ❌ insecure code - the token is merely decoded
// but not verified. anyone can send a token with any id
const decoded = jwt.decode(token);
// âś… secure code - the token is verified
// const decoded = jwt.verify(token, SECRET);
const user = getUserById(decoded.id);
res.json(user);
});
function getUserById(id) {
return { id, name: 'Alice' };
}