Credential Stuffing with Api Keys
How Credential Stuffing Manifests in Api Keys
Credential stuffing in API keys occurs when attackers use automated tools to test stolen or leaked API keys across multiple services. Unlike traditional credential stuffing with usernames and passwords, API key attacks exploit the fact that developers often reuse keys across different environments or services, and keys may be exposed through code repositories, configuration files, or client-side applications.
The most common attack pattern involves an attacker obtaining a valid API key through a data breach, GitHub commit history, or client-side JavaScript exposure. They then use automated scripts to test this key against multiple API endpoints, looking for successful authentication. Since many organizations use the same key across staging, development, and production environments, a key compromised in one context often works in others.
API keys are particularly vulnerable because they're often transmitted in HTTP headers or query parameters, making them susceptible to network interception if not properly encrypted. Attackers can use tools like Burp Suite or custom scripts to rapidly test thousands of keys per minute. The attack surface expands when keys are embedded in mobile applications or browser extensions, where reverse engineering is trivial.
A critical vulnerability is the lack of rate limiting on key validation endpoints. Many APIs allow unlimited attempts to validate keys without triggering security measures, enabling attackers to build databases of working keys for future exploitation. This becomes especially dangerous when combined with API keys that have broad permissions across multiple services.
// Vulnerable pattern: key validation without rate limiting
router.post('/validate-key', (req, res) => {
const { apiKey } = req.body;
const isValid = validateApiKey(apiKey); // No rate limiting
res.json({ valid: isValid });
});
// Attacker script example
const axios = require('axios');
const keysToTest = [
'sk-1234567890abcdef',
'pk-abcdefghijklmnop',
// ... thousands more
];
async function testKeys() {
for (const key of keysToTest) {
try {
const response = await axios.post('https://api.example.com/validate-key', {
apiKey: key
});
if (response.data.valid) {
console.log(`VALID KEY FOUND: ${key}`);
}
} catch (error) {
// Continue testing
}
}
}Another manifestation is API keys embedded in client-side code that never expire. When attackers discover these keys through browser dev tools or network analysis, they can immediately use them to access backend services. This is particularly problematic for keys that grant access to sensitive data or allow resource-intensive operations like video processing or AI model inference.
Api Keys-Specific Detection
Detecting credential stuffing in API keys requires a multi-layered approach combining automated scanning with runtime monitoring. The first line of defense is scanning your API endpoints for exposed keys and vulnerable validation patterns.
middleBrick's API security scanner includes specific checks for credential stuffing vulnerabilities. It tests whether your API endpoints allow unlimited key validation attempts, whether keys are transmitted securely, and whether there are patterns that suggest exposed keys in client-side code. The scanner runs 12 parallel security checks including authentication bypass attempts and rate limiting analysis.
For runtime detection, implement comprehensive logging and monitoring of API key usage patterns. Look for unusual authentication patterns such as multiple failed attempts from the same IP, rapid succession of key validation requests, or keys being used from geographically impossible locations. Modern detection systems use machine learning to identify anomalous behavior patterns that indicate automated credential stuffing attacks.
// Detection middleware for API key abuse
const rateLimit = require('express-rate-limit');
// Track key validation attempts
const keyValidationAttempts = new Map();
function credentialStuffingDetection(req, res, next) {
const { apiKey } = req.body;
const clientIP = req.ip;
// Track validation attempts per key
if (apiKey) {
const keyAttempts = keyValidationAttempts.get(apiKey) || [];
keyAttempts.push(Date.now());
keyValidationAttempts.set(apiKey, keyAttempts);
// Remove attempts older than 1 hour
const recentAttempts = keyAttempts.filter(
time => Date.now() - time < 3600000
);
keyValidationAttempts.set(apiKey, recentAttempts);
// Flag suspicious patterns
if (recentAttempts.length > 100) {
console.warn(`Potential credential stuffing on key: ${apiKey}`);
// Trigger additional verification
}
}
next();
}
// Apply to validation endpoint
router.post('/validate-key', credentialStuffingDetection, (req, res) => {
// ... existing validation logic
});
middleBrick's scanning specifically identifies these vulnerabilities by attempting to validate keys with common patterns, testing for rate limiting bypass, and checking for exposed keys in client-side assets. The scanner provides a security risk score (A–F) with prioritized findings and remediation guidance.
Additional detection methods include implementing honeypot API keys that are never valid but trigger alerts when used, monitoring for keys appearing in public repositories using tools like TruffleHog, and using Web Application Firewalls (WAFs) to detect automated key testing patterns.
Api Keys-Specific Remediation
Remediating credential stuffing vulnerabilities in API keys requires implementing defense-in-depth strategies. The most effective approach combines key lifecycle management, rate limiting, and secure key distribution practices.
First, implement strict key rotation policies. API keys should have expiration dates, and developers should be required to rotate keys regularly. Never allow keys that never expire. Use short-lived tokens for sensitive operations and implement automatic key revocation when compromise is suspected.
// Secure API key implementation with rotation
class SecureApiKeyManager {
constructor() {
this.activeKeys = new Map(); // keyId: { key, createdAt, expiresAt }
this.keyRotationInterval = 30 * 24 * 60 * 60 * 1000; // 30 days
}
generateKey(userId) {
const keyId = crypto.randomBytes(16).toString('hex');
const key = crypto.randomBytes(32).toString('base64');
const createdAt = Date.now();
const expiresAt = createdAt + this.keyRotationInterval;
this.activeKeys.set(keyId, { key, createdAt, expiresAt });
// Schedule automatic rotation
setTimeout(() => this.rotateKey(keyId), this.keyRotationInterval);
return { keyId, key, expiresAt };
}
rotateKey(keyId) {
if (this.activeKeys.has(keyId)) {
const oldKey = this.activeKeys.get(keyId);
const newKey = this.generateKeyForUserId(oldKey.userId);
this.activeKeys.set(keyId, newKey);
// Notify user of rotation
this.notifyKeyRotation(keyId, oldKey, newKey);
}
}
validateKey(keyId, key) {
const keyData = this.activeKeys.get(keyId);
if (!keyData || keyData.expiresAt < Date.now()) {
return false;
}
// Constant-time comparison to prevent timing attacks
return crypto.timingSafeEqual(
Buffer.from(key),
Buffer.from(keyData.key)
);
}
}
Implement robust rate limiting on all API endpoints that accept keys. Use different rate limits for different key types based on their privileges. Keys with administrative access should have stricter limits than read-only keys. Implement exponential backoff for repeated failed attempts.
// Rate limiting middleware for API keys
const rateLimit = require('express-rate-limit');
const keyRateLimiter = rateLimit({
windowMs: 15 * 60 * 1000, // 15 minutes
max: 100, // Limit each key to 100 requests per window
keyGenerator: (req) => {
// Rate limit per API key, not per IP
return req.headers['x-api-key'] || req.query.api_key;
},
message: 'Too many requests from this API key',
standardHeaders: true,
legacyHeaders: false,
});
// Different limits for different key types
const adminKeyRateLimiter = rateLimit({
windowMs: 60 * 60 * 1000, // 1 hour
max: 50, // More restrictive for admin keys
keyGenerator: (req) => req.headers['x-api-key'],
});
// Apply rate limiting to routes
router.use('/admin/*', adminKeyRateLimiter);
router.use('/api/*', keyRateLimiter);
Never expose API keys in client-side code. If client-side access is required, use short-lived tokens generated by a backend service. Implement IP whitelisting for sensitive operations and use mutual TLS for high-privilege API communications.
middleBrick's scanner can verify that your API keys are properly protected by testing for exposed keys in client-side assets, checking for weak validation patterns, and ensuring rate limiting is properly implemented. The scanner's findings map directly to OWASP API Security Top 10 recommendations, helping you prioritize remediation efforts.