Protection of sensitive business information within JWT
import express from 'express';
import jwt from 'jsonwebtoken';
const app = express();
app.get('/login', (req, res) => {
const username = req.query.username;
const password = req.query.password;
const token = jwt.sign({ username, password }, 'secretKey');
res.json({ token });
});
app.get('/protected', (req, res) => {
const token = req.headers.authorization?.split(' ')[1];
try {
const decoded = jwt.verify(token, 'secretKey');
const { username, password } = decoded;
res.json({ username, password });
} catch (error) {
res.status(401).json({ message: 'Invalid token' });
}
});
app.listen(3000, () => {
console.log('Server is running on port 3000');
});
The vulnerability in this code lies in the way sensitive information, such as the username and password, is being stored and transmitted within the JSON Web Token (JWT).
In the
/login
route, the code takes the username and password from the request query parameters and signs them using the
jwt.sign
function. This means that the JWT generated will contain the username and password in plain text.
Later, in the
/protected
route, the code extracts the JWT from the
Authorization
header and attempts to verify it using the
jwt.verify
function. If the verification is successful, it proceeds to extract the username and password from the decoded JWT and sends them back in the response.
This approach poses a security risk because JWTs are typically used for authentication and should not contain sensitive information like passwords. By including the password in the JWT, an attacker who gains access to the token can easily obtain the user's password.
To mitigate this vulnerability, it is recommended to remove the sensitive information from the JWT and manage it securely on the server-side. Instead of including the username and password in the JWT payload, you should only include a unique identifier or any other non-sensitive information that can be used to identify the user.
import express from 'express';
import jwt from 'jsonwebtoken';
const app = express();
app.get('/login', (req, res) => {
const username = req.query.username;
const password = req.query.password;
// Store the sensitive information securely on the server-side
// Generate the JWT with non-sensitive information
const token = jwt.sign({ username }, 'secretKey');
res.json({ token });
});
app.get('/protected', (req, res) => {
const token = req.headers.authorization?.split(' ')[1];
try {
// Verify the JWT signature and integrity
const decoded = jwt.verify(token, 'secretKey');
const { username } = decoded;
// Retrieve the sensitive information from the server-side
const password = getPasswordByUsername(username);
res.json({ username, password });
} catch (error) {
res.status(401).json({ message: 'Invalid token' });
}
});
function getPasswordByUsername(username: string): string {
// Retrieve the password from the server-side based on the username
// Implement your logic here
return 'password';
}
app.listen(3000, () => {
console.log('Server is running on port 3000');
});
The fixed code addresses the vulnerability by removing sensitive information from the JWT and managing it securely on the server-side.
In the
/login
route, the username and password are obtained from the request query parameters. However, instead of storing them in the JWT, the sensitive information is securely stored on the server-side. Only non-sensitive information, such as the username, is included in the JWT. The JWT is generated using the
jwt.sign
function, which takes the username and a secret key as parameters.
In the
/protected
route, the token is extracted from the
Authorization
header of the request. The JWT signature and integrity are verified using the
jwt.verify
function, which takes the token and the secret key as parameters. If the verification is successful, the username is extracted from the decoded token. The sensitive information, in this case, the password, is retrieved from the server-side using the
getPasswordByUsername
function. The username and password are then sent back in the response.
The
getPasswordByUsername
function is responsible for retrieving the password from the server-side based on the username. This function should be implemented with appropriate logic to securely retrieve the password.
By removing sensitive information from the JWT and managing it securely on the server-side, the fixed code mitigates the risk of business information leakage.