Rate Limiting Bypass with Basic Auth
How Rate Limiting Bypass Manifests in Basic Auth
Rate limiting bypass in Basic Auth environments typically exploits the stateless nature of the authentication mechanism. Since Basic Auth credentials are sent with every request in the Authorization header, attackers can rotate through different credential sets to distribute their request volume across multiple authenticated sessions.
A common attack pattern involves credential stuffing with known username/password combinations. An attacker might have a list of 100 valid Basic Auth credentials and distribute their 1,000 requests across these accounts, effectively multiplying their request capacity by 100x. Each credential gets only 10 requests, staying under per-user rate limits while the aggregate attack remains undetected.
Another manifestation occurs when Basic Auth is combined with API keys in the same endpoint. Attackers can alternate between using Basic Auth credentials and API keys, exploiting separate rate limit counters for each authentication method. This creates a situation where the same client can make twice as many requests by switching authentication schemes.
Time-based bypass techniques also work against Basic Auth endpoints. Since Basic Auth implementations often cache authentication decisions for performance, an attacker can exploit timing windows where rate limits reset. By carefully timing requests and rotating credentials, they can maintain continuous access while appearing to stay within limits.
Here's a simplified example of how an attacker might implement credential rotation:
const axios = require('axios');
const credentials = [
'user1:pass1',
'user2:pass2',
'user3:pass3',
// ... more credentials
];
async function makeRequests(targetUrl, totalRequests) {
for (let i = 0; i < totalRequests; i++) {
const credential = credentials[i % credentials.length];
const encoded = Buffer.from(credential).toString('base64');
try {
await axios.get(targetUrl, {
headers: {
'Authorization': `Basic ${encoded}`
}
});
} catch (error) {
console.log(`Request ${i} failed:`, error.message);
}
}
}
This approach distributes requests across multiple authenticated sessions, potentially bypassing per-credential rate limits while maintaining high throughput against the API.
Basic Auth-Specific Detection
Detecting rate limiting bypass in Basic Auth environments requires monitoring authentication patterns across multiple dimensions. The most effective detection looks for unusual credential rotation patterns where the same IP address authenticates with numerous different Basic Auth credentials within a short timeframe.
Key detection signals include:
- Rapid credential cycling from single source IP
- Multiple successful Basic Auth authentications from same IP in under 1 second
- Credential reuse patterns that suggest automated rotation
- Authentication success rates that deviate from normal user behavior
- Requests that maintain high throughput while staying under per-credential limits
Network-level monitoring should track the Authorization header values across requests. Since Basic Auth credentials are base64-encoded, detection systems need to decode and analyze the username:password patterns. Look for statistical anomalies like a single IP successfully authenticating with 50+ unique credential pairs in 60 seconds.
middleBrick's black-box scanning approach specifically tests for this vulnerability by attempting credential rotation patterns against Basic Auth endpoints. The scanner maintains request logs that show authentication success rates and timing patterns, helping identify whether an endpoint is vulnerable to distributed credential attacks.
For API owners, implementing detection requires logging and analyzing authentication events. Here's an example of middleware that could detect suspicious patterns:
const rateLimit = require('express-rate-limit');
const basicAuth = require('basic-auth');
const authTracker = new Map();
function basicAuthRateLimit(req, res, next) {
const credentials = basicAuth(req);
if (!credentials) return next();
const ip = req.ip;
const key = `${ip}:${credentials.name}`;
if (!authTracker.has(key)) {
authTracker.set(key, { count: 0, timestamp: Date.now() });
}
const record = authTracker.get(key);
record.count++;
// Check for credential rotation patterns
const recentAuths = Array.from(authTracker.entries())
.filter(([k, v]) => k.startsWith(ip) && v.timestamp > Date.now() - 60000)
.length;
if (recentAuths > 20) {
console.warn(`Suspicious auth pattern from ${ip}: ${recentAuths} unique credentials in last minute`);
return res.status(429).json({ error: 'Rate limit exceeded' });
}
next();
}
This middleware tracks authentication patterns and can detect when a single IP is cycling through many credentials, a key indicator of rate limiting bypass attempts.
Basic Auth-Specific Remediation
Remediating rate limiting bypass in Basic Auth environments requires implementing authentication-aware rate limiting that tracks credentials rather than just IP addresses or API keys. The fundamental principle is to ensure that rate limits apply to the authenticated identity, not just the transport mechanism.
The most effective approach uses a sliding window rate limiter that tracks requests per credential pair. Here's an implementation using Redis for distributed tracking:
const basicAuth = require('basic-auth');
const redis = require('redis');
const client = redis.createClient();
async function rateLimitedBasicAuth(req, res, next) {
const credentials = basicAuth(req);
if (!credentials) {
return res.status(401).json({ error: 'Authentication required' });
}
const authKey = `auth:${credentials.name}:${Buffer.from(credentials.pass).toString('base64')}`;
const window = 60000; // 1 minute window
const maxRequests = 100;
try {
const current = await client.incr(authKey);
if (current === 1) {
await client.expire(authKey, Math.ceil(window / 1000));
}
const ttl = await client.ttl(authKey);
if (current > maxRequests) {
return res.status(429).json({
error: 'Rate limit exceeded',
retryAfter: ttl
});
}
res.setHeader('X-RateLimit-Remaining', maxRequests - current);
res.setHeader('X-RateLimit-Reset', Date.now() + (ttl * 1000));
next();
} catch (error) {
console.error('Rate limiting error:', error);
next();
}
}
This implementation ensures that rate limits are tied to the actual Basic Auth credentials rather than being bypassable through credential rotation. Each unique username:password combination gets its own rate limit counter.
For enhanced security, combine credential-based rate limiting with IP-based limits to create a multi-dimensional approach:
async function enhancedRateLimit(req, res, next) {
const credentials = basicAuth(req);
const ip = req.ip;
if (!credentials) return next();
const promises = [
getClientRateLimit(ip),
getCredentialRateLimit(credentials.name, credentials.pass)
];
const [ipLimit, credLimit] = await Promise.all(promises);
if (ipLimit.remaining <= 0 || credLimit.remaining <= 0) {
return res.status(429).json({ error: 'Rate limit exceeded' });
}
await Promise.all([
updateClientRateLimit(ip),
updateCredentialRateLimit(credentials.name, credentials.pass)
]);
next();
}
This dual-layer approach prevents both credential rotation attacks and distributed attacks from multiple sources using the same credentials.
Additional remediation strategies include implementing exponential backoff for repeated authentication failures, adding CAPTCHAs after suspicious authentication patterns, and using challenge-response mechanisms that make credential stuffing more difficult. The key is ensuring that authentication state is properly tracked and that rate limits cannot be circumvented by simply rotating through different credential sets.
Related CWEs: resourceConsumption
| CWE ID | Name | Severity |
|---|---|---|
| CWE-400 | Uncontrolled Resource Consumption | HIGH |
| CWE-770 | Allocation of Resources Without Limits | MEDIUM |
| CWE-799 | Improper Control of Interaction Frequency | MEDIUM |
| CWE-835 | Infinite Loop | HIGH |
| CWE-1050 | Excessive Platform Resource Consumption | MEDIUM |