Secure session management
import express from 'express';
import jwt from 'jsonwebtoken';
const app = express();
// Secret key for JWT token
const secretKey = 'mysecretkey';
// Route to generate a new session token
app.get('/login', (req, res) => {
const user = {
id: 1,
username: 'example_user',
role: 'admin'
};
// Generate a new JWT token with a 1-hour expiration time
const token = jwt.sign(user, secretKey, { expiresIn: '1h' });
res.json({ token });
});
// Route to perform a query with an expired token
app.get('/query', (req, res) => {
const token = req.headers.authorization?.split(' ')[1];
try {
// Verify the token and extract the payload
const payload = jwt.verify(token, secretKey);
// Perform the query using the payload data
// ...
res.json({ message: 'Query successful' });
} catch (error) {
res.status(401).json({ message: 'Invalid or expired token' });
}
});
app.listen(3000, () => {
console.log('Server is running on port 3000');
});
The vulnerability in this code is related to insecure session management. Specifically, it allows for the modification of the expiration time of an expired session token, making it possible to reuse the token and continue querying the application.
In the code, the
/login
route generates a new session token using the
jwt.sign
function from the
jsonwebtoken
library. The token is signed with a secret key and has an expiration time of 1 hour.
The
/query
route is used to perform a query with a token provided in the request headers. The token is extracted from the
Authorization
header and verified using the
jwt.verify
function. If the token is valid and has not expired, the query is performed and a successful response is sent. Otherwise, an error response is returned.
However, the vulnerability lies in the fact that the code does not check if the token has expired before performing the query. This means that even if the token has expired, it can still be used to make queries to the application. An attacker can modify the expiration time of an expired token and continue to query the application with it.
To fix this vulnerability, the code should include a check to ensure that expired tokens are not reused in future requests. Once a token has expired, it should not be accepted for further queries.
import express from 'express';
import jwt from 'jsonwebtoken';
const app = express();
// Secret key for JWT token
const secretKey = 'mysecretkey';
// Route to perform a query with a token
app.get('/query', (req, res) => {
const token = req.headers.authorization?.split(' ')[1];
try {
// Verify the token and extract the payload
const payload = jwt.verify(token, secretKey, { ignoreExpiration: true });
// Check if the token is expired
const isTokenExpired = Date.now() >= payload.exp * 1000;
if (isTokenExpired) {
res.status(401).json({ message: 'Invalid or expired token' });
} else {
// Perform the query using the payload data
// ...
res.json({ message: 'Query successful' });
}
} catch (error) {
res.status(401).json({ message: 'Invalid or expired token' });
}
});
app.listen(3000, () => {
console.log('Server is running on port 3000');
});
The fixed code addresses the vulnerability related to insecure session management by implementing proper token expiration handling. Here's how the code works:
1. The code imports the necessary modules, including Express for creating the server and JWT for token handling.
2. The
app
object is created using Express.
3. A secret key is defined for JWT token generation and verification.
4. The
/query
route is created to handle queries with a token.
5. Inside the route handler, the token is extracted from the
Authorization
header using optional chaining (
?.
) and splitting the header value.
6. The code then attempts to verify the token and extract the payload using the
jwt.verify
method. The
ignoreExpiration
option is set to
true
to bypass the default token expiration check.
7. After verifying the token, the code checks if the token is expired by comparing the current time (
Date.now()
) with the expiration time (
payload.exp * 1000
). The expiration time is multiplied by 1000 to convert it from seconds to milliseconds.
8. If the token is expired, the server responds with a 401 status code and a JSON message indicating an invalid or expired token.
9. If the token is not expired, the code proceeds to perform the query using the payload data. This is where you would add your logic to handle the query.
10. Finally, if the query is successful, the server responds with a JSON message indicating the success.
11. The code also includes a catch block to handle any errors that occur during token verification. In case of an error, the server responds with a 401 status code and a JSON message indicating an invalid or expired token.
12. The server is then set to listen on port 3000.
In summary, the fixed code properly verifies the token, checks for token expiration, and handles expired tokens by returning an appropriate response. This ensures that expired tokens cannot be reused to continue querying the application.