HIGH rate limiting bypassexpressbasic auth

Rate Limiting Bypass in Express with Basic Auth

Rate Limiting Bypass in Express with Basic Auth — how this specific combination creates or exposes the vulnerability

In Express, combining Basic Authentication with insufficient or misconfigured rate limiting can create a Rate Limiting Bypass that allows an authenticated or unauthenticated attacker to exceed intended request thresholds. Basic Auth typically validates credentials on each request by decoding an Authorization header, but if rate limiting is applied only after authentication or uses weak keying, attackers can bypass limits by manipulating credentials or leveraging account enumeration.

One common pattern is applying express-rate-limit globally without differentiating limits per authentication context. For example, if the limit is set on the app or a shared middleware before route handling, an attacker may cycle through valid and invalid credentials to avoid lockout or trigger different rate-limit buckets. Because Basic Auth sends credentials with every request, an attacker can rotate usernames or passwords to generate multiple distinct identities, each subject to the same limit window, effectively multiplying allowed requests.

Another bypass vector involves how the limit key is constructed. If the key relies on IP address alone (keyGenerator(req) returning req.ip), an attacker behind a shared proxy or using tokenized authentication can bypass the limit by changing source IPs. When Basic Auth is used without binding the rate limit to the authenticated user identity (e.g., after successful verification), unauthenticated probes can hit endpoints at higher speeds because the limit is not scoped to a user or API key.

Express middleware ordering matters critically. If routes that perform authentication are placed before the rate limiter, the limiter may count only requests that reach authentication, allowing pre-auth requests to slip through. Conversely, if the rate limiter runs before authentication, it might count all requests, but weak keying or missing differentiation between authenticated and unauthenticated contexts can still permit abuse. Attackers exploit this by flooding unauthenticated paths to exhaust resources or infer valid accounts via timing differences, then pivot to authenticated sessions with rotated credentials to evade per-user caps.

Real-world attack patterns mirror findings from OWASP API Top 10 and CWE classifications, such as CWE-770 (Insufficient Rate Limiting). Tools like middleBrick can detect these risks by scanning unauthenticated attack surfaces and mapping findings to compliance frameworks including OWASP API Top 10. Its checks run in 5–15 seconds, testing endpoints without credentials and highlighting whether rate limiting is tied to authentication context, helping you identify gaps before attackers do.

Basic Auth-Specific Remediation in Express — concrete code fixes

To secure Express APIs using Basic Authentication, align rate limiting with authenticated identity and ensure middleware ordering prevents bypass. Below is a concrete, working example that ties rate limits to verified user identity and avoids common misconfigurations.

const express = require('express');
const rateLimit = require('express-rate-limit');
const basicAuth = require('express-basic-auth');

const app = express();

// Store authenticated user limits per identity
const authenticatedLimiter = rateLimit({
  windowMs: 15 * 60 * 1000, // 15 minutes
  max: 100, // limit each authenticated user to 100 requests per window
  keyGenerator: (req) => {
    // Use authenticated user id when available
    if (req.auth && req.auth.user) {
      return req.auth.user;
    }
    // Fallback to IP for unauthenticated checks
    return req.ip;
  },
  skip: (req) => !req.auth, // apply only when auth is verified
  message: { error: 'Too many requests, authenticated user limit exceeded.' },
  standardHeaders: true,
  legacyHeaders: false,
});

// Global unauthenticated limiter (stricter)
const unauthenticatedLimiter = rateLimit({
  windowMs: 15 * 60 * 1000,
  max: 30,
  keyGenerator: (req) => req.ip,
  skip: (req) => !!req.auth,
  message: { error: 'Too many requests, unauthenticated limit exceeded.' },
  standardHeaders: true,
  legacyHeaders: false,
});

// Basic Auth setup with verified user info
app.use(basicAuth({
  users: { 'alice': 'secret1', 'bob': 'secret2' },
  challenge: true,
  authorize: (username, password) => {
    // Replace with secure credential verification
    const valid = (username === 'alice' && password === 'secret1') ||
                  (username === 'bob' && password === 'secret2');
    if (valid) return { user: username };
    return false;
  },
}));

// Apply unauthenticated limiter first to catch abuse before auth
app.use(unauthenticatedLimiter);

// Apply authenticated limiter after auth decisions
app.use((req, res, next) => {
  if (req.auth) {
    return authenticatedLimiter(req, res, next);
  }
  next();
});

app.get('/api/data', (req, res) => {
  res.json({ message: 'Success', user: req.auth ? req.auth.user : 'anonymous' });
});

app.listen(3000, () => console.log('Server running on port 3000'));
  • Middleware ordering: Place unauthenticated limits before authentication checks to block floods early. Then apply authenticated limits after credentials are verified to scope requests per identity.
  • Key granularity: Use a stable identifier (e.g., username or user ID) in keyGenerator for authenticated limits. Avoid relying solely on IP when credentials are present.
  • Separate limits: Maintain distinct limits for authenticated and unauthenticated paths to prevent privilege escalation through credential cycling.
  • Validation before auth: Ensure your authorize function performs constant-time checks where possible to reduce timing leaks that could aid enumeration.

For teams using middleBrick, the scanner’s checks align with these practices, reporting whether rate limiting is appropriately bound to authentication context. On the Pro plan, continuous monitoring can alert you if deviations appear, and the GitHub Action can fail builds when risky configurations are detected in CI/CD.

Related CWEs: resourceConsumption

CWE IDNameSeverity
CWE-400Uncontrolled Resource Consumption HIGH
CWE-770Allocation of Resources Without Limits MEDIUM
CWE-799Improper Control of Interaction Frequency MEDIUM
CWE-835Infinite Loop HIGH
CWE-1050Excessive Platform Resource Consumption MEDIUM

Frequently Asked Questions

Why does rotating Basic Auth credentials not guarantee safety if rate limits are IP-based?
If rate limiting uses only IP as the key, attackers can rotate credentials and source IPs to multiply allowed requests. Effective remediation ties the limit key to authenticated user identity and enforces separate limits for authenticated versus unauthenticated contexts.
Can middleBrick detect a Rate Limiting Bypass in an Express API using Basic Auth?
Yes. middleBrick scans unauthenticated attack surfaces and checks whether rate limiting is scoped to authentication context. Findings map to frameworks like OWASP API Top 10 and include prioritized remediation guidance.