HIGH regex dosexpressbasic auth

Regex Dos in Express with Basic Auth

Regex Dos in Express with Basic Auth — how this specific combination creates or exposes the vulnerability

A Regular Expression Denial of Service (ReDoS) occurs when an attacker can supply input that causes a regular expression to exhibit catastrophic backtracking, consuming excessive CPU time and potentially blocking the event loop. In Express applications that use HTTP Basic Authentication, the combination of parsing the Authorization header and validating credentials with regular expressions can introduce ReDoS conditions.

Consider a typical Express implementation that manually parses and validates the Basic Auth header using a custom regex. The Authorization header value has the format Basic base64(credentials), where credentials are username:password. A developer might write a regex to validate the format of the credentials or to extract parts of it. If the regex is poorly constructed—for example, using nested quantifiers on unbounded input without proper anchoring or length limits—it becomes vulnerable to crafted payloads that cause exponential backtracking.

For instance, a regex like /^(?:[a-zA-Z0-9+\/=]+):(?:[a-zA-Z0-9+\/=]+)$/ applied to user-controlled input may appear safe, but if extended with optional groups or alternations that overlap, it can become exploitable. An attacker can send a malformed or long Authorization header such as Basic dGVzdDp5eXl5eXl5eXl5eXl5eXl5eXl5eXl5eXl5eXl5eXl5eXl5eXl5eXl5eXl5 designed to maximize backtracking within the regex engine. Because Basic Auth credentials are base64-encoded, many invalid characters are tolerated by the decoder, allowing long, repetitive strings that amplify the issue.

In the context of middleware, if the regex is applied on every request and runs synchronously (as many native JavaScript regex implementations do), a single malicious request can block the Node.js event loop for enough time to cause noticeable latency or timeouts for other legitimate requests. This is especially impactful when the regex is evaluated before any early exit checks, such as verifying the presence of the Authorization header prefix. Without request size or header length limits enforced at the network or framework level, the vulnerable regex becomes the primary attack surface.

Moreover, when integrating with other checks, such as verifying credentials against a data store, the ReDoS can be chained: the regex parsing step slows down first, and if followed by an expensive operation (like a database lookup), the performance degradation is compounded. Because middleBrick scans unauthenticated endpoints and flags input validation risks, it can detect patterns where regex-based validation is used without safeguards such as length constraints or non-backtracking patterns, helping to highlight this class of issue even in basic authentication flows.

Basic Auth-Specific Remediation in Express — concrete code fixes

To mitigate ReDoS in Express with Basic Auth, focus on avoiding complex regular expressions over unbounded input and enforcing strict structural limits. Prefer built-in parsing and constant-time comparison where possible, and ensure that any regex used is non-backtracking or constrained by length.

Example 1: Safe parsing with built-in APIs and early validation

Use Node.js’s built-in Buffer to decode the header and validate structure without regex. This avoids backtracking entirely.

const express = require('express');
const app = express();

const SAFE_USERNAME_LENGTH = 128;
const SAFE_PASSWORD_LENGTH = 256;

app.use((req, res, next) => {
  const authHeader = req.headers.authorization;
  if (!authHeader || !authHeader.startsWith('Basic ')) {
    return res.status(401).send('Unauthorized');
  }

  const base64Creds = authHeader.slice(6);
  let creds;
  try {
    creds = Buffer.from(base64Creds, 'base64').toString('utf8');
  } catch (err) {
    return res.status(400).send('Bad request');
  }

  const separatorIndex = creds.indexOf(':');
  if (separatorIndex === -1) {
    return res.status(400).send('Bad request');
  }

  const username = creds.slice(0, separatorIndex);
  const password = creds.slice(separatorIndex + 1);

  if (
    username.length === 0 ||
    username.length > SAFE_USERNAME_LENGTH ||
    password.length === 0 ||
    password.length > SAFE_PASSWORD_LENGTH
  ) {
    return res.status(400).send('Bad request');
  }

  // Proceed to verify username/password, e.g., constant-time compare
  req.auth = { username, password };
  next();
});

app.get('/protected', (req, res) => {
  res.json({ message: 'Access granted' });
});

app.listen(3000);

Example 2: If regex is required, keep it simple and bounded

If you must use a regex, ensure it is anchored, uses non-capturing groups, avoids nested quantifiers, and constrains input length. For example, allow only alphanumeric and a few safe characters within strict length bounds.

const express = require('express');
const app = express();

// A bounded, non-backtracking pattern: only safe chars, max lengths enforced
const USER_REGEX = /^[-a-zA-Z0-9_]{1,64}$/;
const PASS_REGEX = /^[-a-zA-Z0-9!@#$%^&*()_+=]{1,128}$/;

app.use((req, res, next) => {
  const authHeader = req.headers.authorization;
  if (!authHeader || !authHeader.startsWith('Basic ')) {
    return res.status(401).send('Unauthorized');
  }

  const base64Creds = authHeader.slice(6);
  let creds;
  try {
    creds = Buffer.from(base64Creds, 'base64').toString('utf8');
  } catch (err) {
    return res.status(400).send('Bad request');
  }

  const separatorIndex = creds.indexOf(':');
  if (separatorIndex === -1) {
    return res.status(400).send('Bad request');
  }

  const username = creds.slice(0, separatorIndex);
  const password = creds.slice(separatorIndex + 1);

  if (!USER_REGEX.test(username) || !PASS_REGEX.test(password)) {
    return res.status(400).send('Bad request');
  }

  req.auth = { username, password };
  next();
});

app.get('/safe', (req, res) => {
  res.json({ ok: true });
});

app.listen(3001);

General recommendations

  • Avoid regex for parsing structured credentials when simpler string operations suffice.
  • Enforce maximum lengths on header values and on extracted username/password before any processing.
  • Use constant-time comparison for credentials to avoid timing attacks, and validate input strictly.
  • Apply middleware early so malformed or suspicious headers are rejected before expensive operations.

These steps reduce the attack surface for ReDoS and help ensure that Basic Auth handling does not become a bottleneck or vector for denial of service.

Related CWEs: inputValidation

CWE IDNameSeverity
CWE-20Improper Input Validation HIGH
CWE-22Path Traversal HIGH
CWE-74Injection CRITICAL
CWE-77Command Injection CRITICAL
CWE-78OS Command Injection CRITICAL
CWE-79Cross-site Scripting (XSS) HIGH
CWE-89SQL Injection CRITICAL
CWE-90LDAP Injection HIGH
CWE-91XML Injection HIGH
CWE-94Code Injection CRITICAL

Frequently Asked Questions

Can a ReDoS in Basic Auth parsing affect authenticated endpoints only, or unauthenticated ones too?
It can affect unauthenticated endpoints if the application parses the Authorization header on every request. middleBrick scans unauthenticated attack surfaces and can flag regex-based validation in such paths, even when no credentials are required.
Does using Basic Auth over HTTPS prevent ReDoS, or is the regex still a concern?
Transport-layer encryption does not prevent ReDoS. The vulnerability is in how the server parses and validates the header, regardless of encryption. Secure transport protects credential leakage but does not mitigate CPU exhaustion from malicious regex patterns.