Broken Authentication

Broken Authentication in Node.js (Express) Guide [GHSA-5c57-rqjx-35g2]

[Updated May 2026] Updated GHSA-5c57-rqjx-35g2

Overview

Broken Authentication can enable attackers to impersonate users, hijack sessions, or take over accounts, leading to data exfiltration, privilege escalation, and service misuse. In production Node.js/Express apps, weak token handling, predictable session IDs, or insecure storage of credentials enable attackers to bypass login or retain access even after logout. CVE IDs are not provided for this general guide (CVE IDs: N/A). In real-world deployments, misconfigurations such as insecure cookies, lack of token verification, and overly permissive session lifetimes frequently precede exploitation. Recognizing these patterns helps prevent account compromise and downstream data loss. No CVEs are cited here because this guide targets common architectural weaknesses rather than a single vulnerability instance. The impact remains severe: attacker-controlled access can enable data theft, lateral movement, and abuse of privileged functionality within the application.

Code Fix Example

Node.js (Express) API Security Remediation
Vulnerable:
const express = require('express');
const bodyParser = require('body-parser');
const jwt = require('jsonwebtoken');
const app = express();
app.use(bodyParser.json());

app.post('/login', (req, res) => {
  const user = db.findUser(req.body.username);
  if (user && user.password === req.body.password) {
    // Vulnerable: hard-coded secret, no expiration, and token stored in a non-httpOnly cookie
    const token = jwt.sign({ id: user.id }, 'secret');
    res.cookie('token', token, { httpOnly: false, secure: false });
    res.json({ token: token });
  } else {
    res.status(401).send('Unauthorized');
  }
});

app.get('/protected', (req, res) => {
  // No verification of token - anyone can access
  res.send('protected data');
});

app.listen(3000);

Fixed:
const express = require('express');
const cookieParser = require('cookie-parser');
const jwt = require('jsonwebtoken');
require('dotenv').config();
const app = express();
app.use(express.json());
app.use(cookieParser());
const JWT_SECRET = process.env.JWT_SECRET;

app.post('/login', (req, res) => {
  const user = db.findUser(req.body.username);
  if (user && user.password === req.body.password) {
    const token = jwt.sign({ id: user.id }, JWT_SECRET, { expiresIn: '15m', issuer: 'myapp' });
    res.cookie('token', token, { httpOnly: true, secure: true, sameSite: 'strict', maxAge: 15 * 60 * 1000 });
    res.json({ token: token });
  } else {
    res.status(401).send('Unauthorized');
  }
});

function authGuard(req, res, next) {
  const token = req.cookies.token;
  if (!token) return res.status(401).send('Unauthorized');
  try {
    const payload = jwt.verify(token, JWT_SECRET);
    req.userId = payload.id;
    next();
  } catch (e) {
    res.status(401).send('Unauthorized');
  }
}
app.get('/protected', authGuard, (req, res) => {
  res.send('protected data for ' + req.userId);
});

app.listen(3000);

CVE References

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