HIGH session fixationexpressbearer tokens

Session Fixation in Express with Bearer Tokens

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

Session fixation occurs when an application forces a user to use a session identifier that the attacker knows or can choose. In Express APIs that rely on Bearer tokens (typically sent via the Authorization header), fixation can arise when a token is issued before authentication and then reused after authentication without being rotated. If an endpoint accepts a token provided by the client—such as during a token exchange or when a client-supplied token is accepted as-is—attackers can set a known token in a victim’s browser or client and later reuse it after the victim logs in.

Consider an Express route that allows a client to supply a bearer_token for later use:

app.post('/link-token', (req, res) => {
  const { bearer_token } = req.body;
  // Dangerous: accepting a client-supplied token
  req.session.bearer_token = bearer_token;
  res.json({ message: 'token linked' });
});

Here, an attacker can craft a link like https://api.example.com/link-token?bearer_token=attacker_chosen or embed a form on a malicious site that posts a fixed token. After the victim authenticates (e.g., via a separate login flow), the server associates the attacker-chosen token with the authenticated user session. The attacker can then reuse that token to access protected resources, because the server treats the bearer token as proof of authentication without additional validation or rotation.

Even when using Authorization headers rather than cookies, fixation can occur if the API accepts a bearer token from an unauthenticated context and then promotes it to an authenticated context without re-issuing a new token. For example, an OAuth-style token exchange that accepts a bearer token in the request body or header and then returns the same token in a 200 response can enable fixation if the token was previously controlled by an attacker.

Real-world parallels exist in frameworks where tokens are not bound to a fresh authenticated session. Without proper token lifecycle management—such as issuing a new token upon successful authentication and invalidating the pre-authentication token—an attacker’s fixed token remains valid. This maps to OWASP API Top 10 2023:2023-Broken Object Level Authorization (BOLA) when token usage is not tightly scoped and rotated, and can be observed in scenarios where IDOR occurs because the token does not change after privilege elevation.

To detect such patterns, middleBrick scans unauthenticated attack surfaces and correlates OpenAPI/Swagger specs (2.0, 3.0, 3.1) with runtime behavior, flagging endpoints that accept client-supplied bearer tokens without re-issuance or proper invalidation. Findings include severity, contextual guidance, and mappings to compliance frameworks such as OWASP API Top 10 and SOC2.

Bearer Tokens-Specific Remediation in Express — concrete code fixes

Remediation focuses on ensuring bearer tokens are not accepted from unauthenticated clients and are rotated upon authentication. Avoid storing client-supplied bearer tokens directly in the session. Instead, issue a new token after successful credentials verification and discard any pre-authentication token.

Secure Express pattern using bearer tokens with explicit authentication before issuing a new token:

const express = require('express');
const jwt = require('jsonwebtoken');
const app = express();
app.use(express.json());

const SECRET = process.env.JWT_SECRET || 'super-secret-key-change-in-prod';

// Login endpoint that issues a fresh token
app.post('/login', (req, res) => {
  const { username, password } = req.body;
  // Validate credentials against your user store (pseudo-code)
  const user = validateUser(username, password);
  if (!user) {
    return res.status(401).json({ error: 'invalid_credentials' });
  }
  // Issue a new token tied to the authenticated user
  const token = jwt.sign({ sub: user.id, username: user.username }, SECRET, { expiresIn: '1h' });
  res.json({ access_token: token, token_type: 'Bearer' });
});

// Protected route that requires a valid bearer token
function authenticateToken(req, res, next) {
  const authHeader = req.headers['authorization'];
  const token = authHeader && authHeader.split(' ')[1];
  if (!token) return res.sendStatus(401);
  jwt.verify(token, SECRET, (err, user) => {
    if (err) return res.sendStatus(403);
  });
  next();
}

app.get('/profile', authenticateToken, (req, res) =&;
  // In a real app, decode and fetch user data
  res.json({ user: 'profile data' });
});

// Do NOT accept a client-supplied token to link or reuse
app.post('/link-token', (req, res) => {
  res.status(400).json({ error: 'do_not_accept_client_supplied_token' });
});

If your integration involves OAuth token exchange, ensure the incoming bearer token is short-lived, one-time use, and not directly reused as the final access token. After exchanging, issue a new token and revoke the original. For SPAs or mobile clients, store tokens securely (e.g., httpOnly cookies with Secure and SameSite, or secure storage) and avoid exposing token-setting logic to client-controlled values.

middleBrick’s LLM/AI Security checks can identify prompt injection attempts that aim to coax token disclosure or misuse, while its standard security scans flag endpoints that accept client-supplied bearer tokens. Pro plans with continuous monitoring can alert you when risky patterns appear, and the GitHub Action can fail builds if risk scores exceed your threshold.

Frequently Asked Questions

Can an attacker fixate a bearer token if the API only uses Authorization headers and no cookies?
Yes. Fixation does not require cookies. If an endpoint accepts a bearer token from an unauthenticated request and then uses that same token to represent an authenticated session, an attacker can supply a known token (e.g., via query parameters, headers, or body) and later authenticate, keeping the same token. Always issue a new token after authentication and reject client-supplied bearer tokens.
What is the best practice for rotating bearer tokens in Express to prevent fixation?
Issue a new, cryptographically random access token upon successful authentication and send it to the client. Invalidate or ignore any token that was provided before authentication. Avoid reusing tokens across authentication boundaries, and enforce short lifetimes with scope-bound claims. middleBrick scans can help identify endpoints that do not rotate tokens by correlating spec definitions with runtime behavior.