Broken Authentication

Broken Authentication in Node.js (Express) [GHSA-f6hc-c5jr-878p]

[Updated Apr 2026] Updated GHSA-f6hc-c5jr-878p

Overview

Broken authentication allows attackers to impersonate legitimate users, bypass login controls, or escalate privileges by stealing or abusing credentials or tokens. In a Node.js Express API, this can lead to account takeover, exposure of personal data, and abusive actions on behalf of users or admins, causing data leaks, financial impact, and service disruption. On Express apps, this vulnerability often arises from weak session management, insecure cookies, or mishandled tokens. Examples include storing session identifiers or user IDs in plaintext cookies, omitting HttpOnly and Secure flags, using long-lived or non-rotating tokens, and performing authentication checks only on the client or weakly on the server. These issues are exacerbated by misconfigurations such as relying on local in-memory sessions, failing to validate JWTs on every request, or allowing credential stuffing without rate limiting. Attackers can hijack sessions or forge tokens to access protected routes, potentially exposing sensitive data or performing privileged actions. Remediation combines strong, verifiable authentication with defense in depth: use server-side sessions or signed/verified tokens, configure cookie flags (HttpOnly, Secure, SameSite), enforce TLS, rate-limit login attempts, rotate session IDs on login, implement MFA, and monitor authentication events for anomalies.

Code Fix Example

Node.js (Express) API Security Remediation
Vulnerable pattern:
const express = require('express');
const cookieParser = require('cookie-parser');
const app = express();
app.use(cookieParser());

// Insecure login that sets a plain cookie with user id (no httpOnly, no signature)
app.post('/login', (req, res) => {
  const { username, password } = req.body;
  const user = findUser(username);
  if (user && user.password === password) {
    res.cookie('session', String(user.id)); // insecure
    return res.json({ ok: true });
  }
  res.status(401).json({ error: 'Invalid credentials' });
});

function auth(req, res, next) {
  const sid = req.cookies.session;
  if (sid) {
    // trust cookie as identity without server-side verification
    req.user = { id: Number(sid) };
    return next();
  }
  res.status(401).json({ error: 'Not authenticated' });
}

app.get('/me', auth, (req, res) => res.json({ user: req.user }));

// ... app.listen(..)


Fixed pattern:
const express = require('express');
const session = require('express-session');
const app = express();
app.use(express.json());

app.use(session({
  name: 'sessionId',
  secret: process.env.SESSION_SECRET || 'change-me',
  resave: false,
  saveUninitialized: false,
  cookie: { httpOnly: true, secure: true, sameSite: 'lax', maxAge: 24 * 60 * 60 * 1000 }
}));

// In secure login, set server-side session
app.post('/login', async (req, res) => {
  const { username, password } = req.body;
  const user = findUser(username);
  if (user && (await verifyPassword(password, user.passwordHash))) {
    req.session.userId = user.id;
    return res.json({ ok: true });
  }
  res.status(401).json({ error: 'Invalid credentials' });
});

function auth(req, res, next) {
  if (req.session && req.session.userId) {
    req.user = { id: req.session.userId };
    return next();
  }
  res.status(401).json({ error: 'Not authenticated' });
}

app.get('/me', auth, (req, res) => res.json({ user: req.user }));

// CSRF protection can be added with csurf, optional here

// app.listen(..)

CVE References

Choose which optional cookies to allow. You can change this any time.