HIGH password sprayingexpress

Password Spraying in Express

How Password Spraying Manifests in Express

Password spraying in Express applications typically exploits authentication endpoints that accept username/password combinations without adequate rate limiting or credential validation. The attack works by attempting a small set of common passwords across many valid usernames, avoiding detection mechanisms that trigger on single-account brute force attempts.

In Express, password spraying commonly targets authentication routes like:

app.post('/login', async (req, res) => {
  const { username, password } = req.body;
  const user = await User.findOne({ where: { username } });
  
  if (!user) {
    // Critical vulnerability: returning early without rate limiting
    return res.status(401).json({ error: 'Invalid credentials' });
  }
  
  const valid = await bcrypt.compare(password, user.password);
  if (!valid) {
    // No rate limiting here either
    return res.status(401).json({ error: 'Invalid credentials' });
  }
  
  // Successful authentication
  const token = jwt.sign({ userId: user.id }, process.env.JWT_SECRET);
  res.json({ token });
});

The vulnerability lies in the lack of rate limiting and the early return pattern. When a username doesn't exist, the endpoint returns immediately without any throttling. Attackers can send thousands of requests with non-existent usernames and common passwords, mapping out valid accounts through timing differences or error responses.

Express middleware like express-rate-limit is often missing or misconfigured. A typical vulnerable setup:

// ❌ INSECURE: No rate limiting on authentication
app.post('/api/auth/login', async (req, res) => {
  const { email, password } = req.body;
  const user = await User.findOne({ where: { email } });
  
  if (!user || !(await bcrypt.compare(password, user.password))) {
    // No delay, no counter, no blocking
    return res.status(401).json({ error: 'Authentication failed' });
  }
  
  res.json({ token: generateToken(user) });
});

Attackers can automate this using tools like Burp Suite or custom scripts, sending requests like:

POST /login HTTP/1.1
Host: example.com
Content-Type: application/json

{
  "username": "admin",
  "password": "Password123!"
}

The lack of consistent response timing between valid and invalid usernames makes detection easier for attackers. Some Express applications inadvertently leak information through response times or error messages, further facilitating password spraying campaigns.

Express-Specific Detection

Detecting password spraying in Express requires monitoring authentication endpoints for specific patterns. middleBrick's black-box scanning approach tests these vulnerabilities without requiring access to source code or credentials.

middleBrick scans Express authentication endpoints by:

  1. Identifying login routes through OpenAPI spec analysis or runtime discovery
  2. Testing with common password lists across multiple usernames
  3. Analyzing response patterns, timing, and error codes
  4. Checking for missing rate limiting middleware
  5. Verifying proper error handling consistency

The scanner specifically looks for Express middleware configurations that might be missing or improperly set up. For example, it checks if express-rate-limit is configured with appropriate windows and limits:

// middleBrick detection patterns
// ❌ MISSING: No rate limiting on /login
app.post('/login', authHandler);

middleBrick's LLM security checks also detect if authentication endpoints are exposed to AI services or if system prompts contain credential-related information that could be leaked through prompt injection attacks.

Real-world detection scenarios include:

Detection PatternRisk LevelmiddleBrick Finding
No rate limiting on /auth/loginHigh"Authentication endpoint lacks rate limiting - vulnerable to password spraying"
Early returns without throttlingHigh"Authentication endpoint returns immediately for invalid credentials"
Inconsistent response timingMedium"Response timing varies between valid/invalid usernames"

middleBrick's continuous monitoring (Pro plan) can track authentication endpoint security over time, alerting when new vulnerabilities are introduced or when attack patterns are detected.

Express-Specific Remediation

Remediating password spraying in Express requires implementing proper rate limiting, consistent error handling, and secure authentication patterns. Here's how to fix these vulnerabilities using Express's native capabilities:

1. Implement rate limiting on all authentication endpoints:

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

// Limit to 5 attempts per 15 minutes per IP
const loginRateLimiter = rateLimit({
  windowMs: 15 * 60 * 1000, // 15 minutes
  max: 5, // limit each IP to 5 requests
  message: {
    error: 'Too many authentication attempts. Try again later.'
  },
  standardHeaders: true,
  legacyHeaders: false,
  keyGenerator: (req) => req.ip
});

// Apply to all auth routes
app.use('/auth/login', loginRateLimiter);
app.use('/auth/forgot-password', loginRateLimiter);
app.use('/auth/reset-password', loginRateLimiter);

2. Use consistent response patterns to prevent timing attacks:

app.post('/login', async (req, res) => {
  const { username, password } = req.body;
  
  // Always perform a database lookup, even for non-existent users
  const user = await User.findOne({ where: { username } }) || { password: '' };
  
  // Simulate hash comparison time for non-existent users
  const exists = user.id !== undefined;
  const valid = await bcrypt.compare(password, user.password);
  
  // Always perform the same operations regardless of outcome
  await logAuthenticationAttempt(username, valid && exists);
  
  if (!valid || !exists) {
    // Add delay to prevent timing attacks
    await delay(300);
    return res.status(401).json({ 
      error: 'Invalid credentials' 
    });
  }
  
  const token = jwt.sign({ userId: user.id }, process.env.JWT_SECRET);
  res.json({ token });
});

// Utility function to add constant delay
function delay(ms) {
  return new Promise(resolve => setTimeout(resolve, ms));
}

3. Implement account lockout mechanisms:

const MAX_FAILED_ATTEMPTS = 5;
const LOCKOUT_DURATION = 30 * 60 * 1000; // 30 minutes

app.post('/login', async (req, res) => {
  const { username } = req.body;
  const user = await User.findOne({ where: { username } });
  
  if (user && user.failedAttempts >= MAX_FAILED_ATTEMPTS) {
    const now = Date.now();
    const lockoutEnd = user.lockoutUntil || 0;
    
    if (now < lockoutEnd) {
      const remaining = Math.ceil((lockoutEnd - now) / 1000);
      return res.status(429).json({
        error: 'Account temporarily locked',
        retryAfter: remaining
      });
    }
    
    // Reset failed attempts after lockout period
    user.failedAttempts = 0;
    await user.save();
  }
  
  // Authentication logic...
});

4. Add CAPTCHA or 2FA after multiple failed attempts:

const captchaMiddleware = require('./captcha-middleware');

app.post('/login', loginRateLimiter, async (req, res, next) => {
  const { username } = req.body;
  const user = await User.findOne({ where: { username } });
  
  if (user && user.failedAttempts >= 3) {
    return captchaMiddleware(req, res, next);
  }
  
  next();
}, authHandler);

middleBrick's GitHub Action integration can automatically scan your Express application's authentication endpoints during CI/CD, failing builds if password spraying vulnerabilities are detected.

Frequently Asked Questions

How can I test if my Express authentication endpoint is vulnerable to password spraying?
You can test using middleBrick's self-service scanner by submitting your API URL. The scanner will automatically test authentication endpoints with common password patterns and check for missing rate limiting. Alternatively, you can manually test by sending multiple authentication requests with different usernames and common passwords while monitoring response times and error codes. Look for endpoints that return immediately without throttling and show timing differences between valid and invalid usernames.
Does middleBrick scan for password spraying vulnerabilities in Express applications?
Yes, middleBrick specifically tests for password spraying vulnerabilities as part of its Authentication category scanning. It checks for missing rate limiting on authentication endpoints, early return patterns without throttling, inconsistent response timing, and other indicators of password spraying vulnerability. The scanner tests with common password lists and analyzes response patterns to identify if your Express application is vulnerable to this attack.