MEDIUM session fixationexpressdynamodb

Session Fixation in Express with Dynamodb

Session Fixation in Express with Dynamodb — how this specific combination creates or exposes the vulnerability

Session fixation occurs when an application assigns a user a session identifier before authentication and does not issue a new identifier after login. In an Express application using Amazon DynamoDB as the session store, this pattern can expose your API if session identifiers are predictable or not rotated on authentication. DynamoDB stores session records keyed by session ID; if the client-supplied ID is reused after login, an attacker who knows or sets that ID can hijack the authenticated session.

Express does not enforce session ID rotation by default. When you use DynamoDB to persist sessions (for example via an HTTP-only cookie), the application must explicitly generate a new, cryptographically random session key after successful authentication and migrate session data to the new key. Without this step, the session fixation risk remains because the attacker’s pre-set session ID remains valid after the victim logs in.

Additionally, if your Express routes read session data directly from DynamoDB using the cookie value without validating freshness or binding the session to the authenticated user’s context, an attacker can craft URLs with a known session ID and trick a victim into authenticating. Because DynamoDB is a remote data store, ensure your session schema includes attributes like issued_at and user_id so you can detect anomalies and enforce rotation.

An example vulnerable pattern: an Express route that sets a cookie with a client-provided session_id and later loads that same ID from DynamoDB after login without regeneration. To detect this via scanning, tools like middleBrick analyze your API surface and flag missing session rotation and weak session management controls.

Dynamodb-Specific Remediation in Express — concrete code fixes

Remediate session fixation by ensuring a new, random session ID is created after authentication and that the old ID is invalidated. When using DynamoDB, store session records with attributes that support rotation and validation. Below are concrete, working examples for Express with the AWS SDK for JavaScript (v3).

const express = require('express');
const cookieParser = require('cookie-parser');
const { DynamoDBClient, GetItemCommand, PutItemCommand, DeleteItemCommand } = require('@aws-sdk/client-dynamodb');
const { marshall, unmarshall } = require('@aws-sdk/util-dynamodb');
const crypto = require('crypto');

const app = express();
app.use(cookieParser());
const client = new DynamoDBClient({ region: 'us-east-1' });
const TABLE = process.env.SESSION_TABLE || 'sessions';

function randomSessionId() {
  return crypto.randomBytes(16).toString('hex');
}

async function createSession(userId) {
  const sessionId = randomSessionId();
  const now = Math.floor(Date.now() / 1000);
  const item = {
    sessionId: { S: sessionId },
    userId: { S: userId },
    issuedAt: { N: String(now) },
    expiresAt: { N: String(now + 7200) } // 2 hours
  };
  await client.send(new PutItemCommand({
    TableName: TABLE,
    Item: item
  }));
  return sessionId;
}

async function getSession(sessionId) {
  const cmd = new GetItemCommand({
    TableName: TABLE,
    Key: marshall({ sessionId })
  });
  const resp = await client.send(cmd);
  return resp.Item ? unmarshall(resp.Item) : null;
}

async function deleteSession(sessionId) {
  await client.send(new DeleteItemCommand({
    TableName: TABLE,
    Key: marshall({ sessionId })
  }));
}

// Login route: rotate session ID after successful credentials validation
app.post('/login', async (req, res) => {
  const { username, password } = req.body;
  // TODO: validate username/password against your user store
  const authenticatedUserId = await validateCredentials(username, password);
  if (!authenticatedUserId) {
    return res.status(401).send('Unauthorized');
  }

  // Optional: delete any pre-authentication session associated with this user
  // (if you had user-to-session mapping)

  // Issue a new session ID and store in DynamoDB
  const newSessionId = await createSession(authenticatedUserId);

  // Set cookie with new ID; do NOT reuse any pre-login cookie name/ID
  res.cookie('sessionId', newSessionId, {
    httpOnly: true,
    secure: process.env.NODE_ENV === 'production',
    sameSite: 'strict',
    maxAge: 7200000
  });
  res.send({ sessionId: newSessionId });
});

// Protected route: validate session and ensure binding
app.get('/profile', async (req, res) => {
  const sessionId = req.cookies?.sessionId;
  if (!sessionId) {
    return res.status(401).send('Unauthorized');
  }
  const session = await getSession(sessionId);
  if (!session || session.expiresAt < String(Math.floor(Date.now() / 1000))) {
    return res.status(401).send('Unauthorized');
  }
  // Optionally re-bind session to user context or refresh TTL here
  res.send({ userId: session.userId });
});

Key remediation points specific to DynamoDB:

  • Generate session IDs using a cryptographically secure random source (crypto.randomBytes) to avoid predictability that could enable fixation.
  • After authentication, always create a new item in DynamoDB with a fresh sessionId and delete or ignore any session identifier the client supplied before login.
  • Include server-side attributes like issuedAt and expiresAt to allow server-side validation and to detect reused or stale sessions.
  • Use conditional writes or TTL where appropriate to enforce expiration and reduce stale session data.

By rotating the session identifier and validating server-side against DynamoDB, you eliminate the fixation risk and ensure each authenticated session is bound to a fresh, unguessable key.

Frequently Asked Questions

How does middleBrick help detect session fixation risks in Express APIs using DynamoDB?
middleBrick scans your API endpoints and maps findings to frameworks like OWASP API Top 10, reporting missing session rotation and weak session management controls. It checks unauthenticated attack surfaces and can surface insecure session handling patterns when reviewing your OpenAPI/Swagger spec and runtime behavior.
Can DynamoDB session stores alone prevent session fixation, or is code-level rotation required?
DynamoDB provides storage, but preventing session fixation requires application logic to generate a new session ID after authentication and to invalidate the pre-login identifier. The scanner helps verify that your API endpoints implement this rotation correctly.