Broken Authentication

Broken Authentication in Node.js (Express) [May 2026] [GHSA-gmvf-9v4p-v8jc]

[Updated May 2026] Updated GHSA-gmvf-9v4p-v8jc

Overview

Broken Authentication is among the most dangerous web application flaws in Node.js (Express) apps. If an attacker can bypass login, hijack sessions, or reuse tokens, they gain privileged access, exfiltrate data, or operate with the victim's identity. In production, this can lead to data breaches, regulatory penalties, and reputational harm. In Express projects, broken authentication often manifests as insecure session management, relying on client-held tokens without verification, or neglecting secure cookie attributes. Common patterns include storing session state in cookies without httpOnly or secure flags, using unsigned tokens or base64-encoded tokens, and failing to verify JWT signatures or expiration. This guide demonstrates how the vulnerability can appear and how to fix it, by replacing an insecure token approach with signed, expiring tokens and robust cookie handling. It also provides remediation steps tailored to Node.js/Express deployments. Note: no CVEs are provided for this generic guide.

Code Fix Example

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

// Fake in-memory user store
const users = { '1': { id: '1', name: 'Alice' } };

// Vulnerable: relies on a client-provided base64-encoded JSON token without signature verification
app.get('/dashboard', (req, res) => {
  const token = req.cookies['auth'];
  if (!token) return res.status(401).send('Unauthorized');
  try {
    const payload = JSON.parse(Buffer.from(token, 'base64').toString());
    const user = users[payload.userId];
    if (user) return res.send(`Hello, ${user.name}`);
  } catch (e) { /* ignore */ }
  return res.status(401).send('Unauthorized');
});

// Fixed: use signed JWTs with proper verification and expiration
const jwt = require('jsonwebtoken');
const JWT_SECRET = process.env.JWT_SECRET || 'dev-secret';

app.get('/dashboard-secure', (req, res) => {
  const token = req.cookies['auth'];
  if (!token) return res.status(401).send('Unauthorized');
  try {
    const payload = jwt.verify(token, JWT_SECRET);
    return res.send(`Hello, ${payload.name}`);
  } catch (e) {
    return res.status(401).send('Unauthorized');
  }
});

app.listen(3000, () => console.log('App listening on port 3000'));

CVE References

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