Skip to main content

Session Fixation

Need

Prevention of session hijacking

Context

  • Usage of TypeScript for statically typed JavaScript development
  • Usage of Express for building web applications and handling HTTP requests

Description

Non compliant code

import express from 'express';

const app = express();

app.use(express.json());

let sessionCookie: string;

app.post('/login', (req, res) => {
const { username, password } = req.body;

// Perform authentication logic here...

// Set the session cookie
sessionCookie = 'user-session-cookie';

res.status(200).json({ message: 'Login successful' });
});

app.get('/dashboard', (req, res) => {
// Check if the session cookie is set
if (sessionCookie) {
// Render the dashboard
res.status(200).json({ message: 'Welcome to the dashboard' });
} else {
res.status(401).json({ message: 'Unauthorized' });
}
});

app.listen(3000, () => {
console.log('Server is running on port 3000');
});

The vulnerability in this code is session fixation. Session fixation occurs when the session cookie remains the same after a successful login, allowing an attacker to hijack the user's session.

In this code, the session cookie is stored in the variable sessionCookie. After a successful login, the session cookie is set to a fixed value 'user-session-cookie'. This means that every user who logs in will have the same session cookie value.

The issue is that the session cookie is not invalidated or changed after a successful login. This allows an attacker to obtain the session cookie value and use it to impersonate the user's session. The attacker can set their own cookie with the known session cookie value and gain unauthorized access to the user's account.

To fix this vulnerability, the code should invalidate the anonymous cookie and create a new one after a successful login. This ensures that the session cookie value is unique for each user and cannot be easily guessed or exploited by an attacker.

Steps

  • Generate a unique session ID for each user upon successful login.
  • Store the session ID in a secure manner, such as in a server-side session store or database.
  • Invalidate the previous session cookie, if any, after successful login.
  • Create a new session cookie with the new session ID and set it in the response headers.
  • Update the sessionCookie variable to store the new session ID.
  • Modify the '/dashboard' route to check the validity of the session ID instead of just checking if the sessionCookie variable is set.
  • If the session ID is valid, render the dashboard. Otherwise, return a 401 Unauthorized response.
  • Consider implementing additional security measures, such as session expiration and session regeneration after a certain period of time.

Compliant code

import express from 'express';
import { v4 as uuidv4 } from 'uuid';

const app = express();

app.use(express.json());

let sessionCookie: string;

app.post('/login', (req, res) => {
const { username, password } = req.body;

// Perform authentication logic here...

// Generate a new session ID
const sessionId = uuidv4();

// Invalidate the previous session cookie, if any
res.clearCookie('session');

// Create a new session cookie with the new session ID
res.cookie('session', sessionId, { httpOnly: true, secure: true });

// Update the sessionCookie variable to store the new session ID
sessionCookie = sessionId;

res.status(200).json({ message: 'Login successful' });
});

app.get('/dashboard', (req, res) => {
// Check if the session ID is valid
const sessionId = req.cookies.session;
if (sessionId && sessionId === sessionCookie) {
// Render the dashboard
res.status(200).json({ message: 'Welcome to the dashboard' });
} else {
res.status(401).json({ message: 'Unauthorized' });
}
});

app.listen(3000, () => {
console.log('Server is running on port 3000');
});

The fixed code addresses the vulnerability by implementing session management best practices. Here's an explanation of the changes made:

  1. The code imports the necessary modules, including the uuid module for generating unique session IDs.

  2. The express application is created and the express.json() middleware is used to parse incoming JSON data.

  3. A variable sessionCookie is declared to store the current session ID.

  4. The /login route is defined to handle the login request. It extracts the username and password from the request body.

  5. After performing the authentication logic (not shown in the code), a new session ID is generated using uuidv4().

  6. The previous session cookie, if any, is invalidated by calling res.clearCookie('session'). This ensures that any existing session cookie is removed from the client's browser.

  7. A new session cookie is created using res.cookie('session', sessionId, { httpOnly: true, secure: true }). The httpOnly flag ensures that the cookie is only accessible via HTTP requests and cannot be accessed by client-side JavaScript. The secure flag ensures that the cookie is only sent over HTTPS connections.

  8. The sessionCookie variable is updated with the new session ID.

  9. A JSON response with a status of 200 and a message indicating successful login is sent back to the client.

  10. The /dashboard route is defined to handle requests to the dashboard. It checks if the session ID stored in the cookie matches the sessionCookie variable. If they match, it sends a JSON response with a status of 200 and a welcome message. Otherwise, it sends a JSON response with a status of 401 indicating unauthorized access.

  11. The server is set to listen on port 3000, and a message is logged to indicate that the server is running.

By invalidating the previous session cookie and generating a new one after a successful login, the fixed code prevents session fixation attacks.

References