Broken Authentication

Broken Authentication in Node.js (Express) [Jun 2026] [GHSA-8783-3wgf-jggf]

[Updated Jun 2026] Updated GHSA-8783-3wgf-jggf

Overview

Broken Authentication can enable attackers to impersonate users, hijack sessions, or escalate privileges, leading to account takeovers or data theft. In real-world Node.js Express apps, weak token handling and insecure cookies commonly enable these attacks. When tokens are stored insecurely or not validated properly, an attacker can reuse a stolen token to access protected resources. This guide covers how such flaws manifest in Express environments and how to remediate them to reduce risk. No CVEs are provided for this request; focus is on general remediation practices that apply across typical Express deployments.

Code Fix Example

Node.js (Express) API Security Remediation
/* Vulnerable pattern (insecure token handling) and fix side by side */

const express = require('express');
const cookieParser = require('cookie-parser');
const jwt = require('jsonwebtoken');
const app = express();
app.use(express.json());
app.use(cookieParser());
const JWT_SECRET = process.env.JWT_SECRET || 'change-me';
const isProd = process.env.NODE_ENV === 'production';

// Vulnerable endpoints: token stored in a regular cookie (no httpOnly/secure/sameSite)
app.post('/login_v', (req, res) => {
  // In a real app, verify user credentials here
  const userId = 1;
  const token = jwt.sign({ userId }, JWT_SECRET, { expiresIn: '15m' });
  // Vulnerable: token placed in a normal cookie
  res.cookie('token', token);
  res.json({ ok: true });
});

app.get('/me_v', (req, res) => {
  const token = req.cookies?.token;
  if (!token) return res.status(401).send('Unauthorized');
  try {
    const payload = jwt.verify(token, JWT_SECRET);
    res.json({ userId: payload.userId });
  } catch {
    res.status(401).send('Unauthorized');
  }
});

// Fixed endpoints: secure cookie attributes and/or token via Authorization header
app.post('/login_f', (req, res) => {
  const userId = 1;
  const token = jwt.sign({ userId }, JWT_SECRET, { expiresIn: '15m' });
  // Secure: HttpOnly, Secure, SameSite, and short expiry
  res.cookie('token', token, {
    httpOnly: true,
    secure: isProd,
    sameSite: 'Strict',
    maxAge: 15 * 60 * 1000
  });
  res.json({ ok: true });
});

function verifyTokenFromRequest(req) {
  const token = req.cookies?.token || (req.headers.authorization?.startsWith('Bearer ') ? req.headers.authorization.split(' ')[1] : null);
  if (!token) return null;
  try { return jwt.verify(token, JWT_SECRET); } catch { return null; }
}
app.get('/me_f', (req, res) => {
  const payload = verifyTokenFromRequest(req);
  if (!payload) return res.status(401).send('Unauthorized');
  res.json({ userId: payload.userId });
});

app.listen(3000, () => console.log('Server running on port 3000'));

CVE References

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