Broken Authentication with Api Keys
How Broken Authentication Manifests in Api Keys
Broken authentication in API keys occurs when attackers can bypass, guess, or exploit the key validation mechanism. In API contexts, this manifests through several specific attack patterns:
# Insecure API key exposure in client-side code
const API_KEY = 'sk-1234567890abcdef';
fetch(`https://api.example.com/data?apikey=${API_KEY}`)
API keys embedded directly in JavaScript become visible to anyone inspecting the network traffic or page source. Attackers can extract these keys and use them from their own systems.
// Weak key validation logic
app.get('/api/data', (req, res) => {
const apiKey = req.headers['x-api-key'];
if (apiKey && apiKey.length > 5) { // Broken validation
return res.json({ data: 'sensitive info' });
}
res.status(401).json({ error: 'Unauthorized' });
});
This validation accepts any key longer than 5 characters, allowing attackers to use simple strings like 'password' or '12345' to gain access.
Another common pattern is predictable key generation:
# Predictable key generation
import uuid
import hashlib
def generate_api_key(user_id):
return hashlib.sha256(f'prefix-{user_id}-suffix'.encode()).hexdigest()
# Attacker can brute force user_id values to generate valid keys
API keys transmitted over HTTP instead of HTTPS expose credentials to network sniffing:
# Insecure transmission
curl http://api.example.com/data -H "Authorization: Bearer $API_KEY"
Missing rate limiting on authentication endpoints enables credential stuffing attacks:
// No rate limiting on key validation
app.post('/api/auth', (req, res) => {
const { apiKey } = req.body;
// No throttling - attacker can try unlimited combinations
if (validateApiKey(apiKey)) {
return res.json({ token: generateJwt(apiKey) });
}
res.status(401).json({ error: 'Invalid API key' });
});Api Keys-Specific Detection
Detecting broken authentication in API keys requires both automated scanning and manual inspection. Here are the specific detection methods:
Network Traffic Analysis
# Capture API key transmission
tcpdump -i eth0 -w api_traffic.pcap port 80 or port 443
# Search for key patterns in captured traffic
strings api_traffic.pcap | grep -E 'sk-[a-zA-Z0-9]{10,}'
Static Code Analysis
# Find exposed API keys in source code
grep -r "sk-[a-zA-Z0-9]\{10,\}" . --include="*.js" --include="*.py" --include="*.java"
grep -r "Bearer [a-zA-Z0-9]\{20,\}" . --include="*.js" --include="*.py"
Automated Scanning with middleBrick
# Scan API endpoint for authentication issues
middlebrick scan https://api.example.com/v1/users
# Scan with OpenAPI spec for deeper analysis
middlebrick scan https://api.example.com/v1/openapi.json
middleBrick's authentication check specifically tests for:
- Missing or weak API key validation
- Predictable key generation patterns
- Exposure of keys in responses or error messages
- Missing rate limiting on authentication endpoints
- API key reuse across different services
Runtime Testing
# Test for predictable key patterns
for i in {1..100}; do
curl -s -H "Authorization: Bearer test$i" https://api.example.com/data | grep -q "sensitive" && echo "Vulnerable to predictable keys"
done
Log Analysis
# Check for repeated failed authentication attempts
grep "Failed authentication" /var/log/api.log | awk '{print $1}' | sort | uniq -c | sort -nr | head -10Api Keys-Specific Remediation
Fixing broken authentication in API keys requires implementing proper validation, secure transmission, and monitoring. Here are specific remediation strategies:
Secure Key Generation and Storage
# Secure random key generation
import secrets
import base64
def generate_secure_api_key():
return base64.urlsafe_b64encode(secrets.token_bytes(32)).decode('utf-8')
# Secure key storage
import redis
redis_client = redis.Redis()
def store_api_key(user_id, api_key):
redis_client.set(f'api_key:{api_key}', user_id, ex=31536000) # 1 year expiry
redis_client.set(f'user:{user_id}:api_key', api_key)
Proper Validation Middleware
// Express.js authentication middleware
const jwt = require('jsonwebtoken');
function apiKeyAuth(req, res, next) {
const apiKey = req.headers['x-api-key'];
if (!apiKey) {
return res.status(401).json({ error: 'API key required' });
}
// Validate key format and existence
if (!apiKey.match(/^[a-zA-Z0-9_-]{32,64}$/)) {
return res.status(401).json({ error: 'Invalid API key format' });
}
// Check key in database or cache
db.query('SELECT user_id FROM api_keys WHERE key = $1', [apiKey])
.then(result => {
if (!result.rows.length) {
return res.status(401).json({ error: 'Invalid API key' });
}
req.user = { id: result.rows[0].user_id };
next();
})
.catch(err => {
res.status(500).json({ error: 'Internal server error' });
});
}
Rate Limiting Implementation
const rateLimit = require('express-rate-limit');
const authLimiter = rateLimit({
windowMs: 15 * 60 * 1000, // 15 minutes
max: 5, // limit each IP to 5 requests per windowMs
message: 'Too many authentication attempts'
});
// Apply to authentication endpoints
app.post('/api/auth', authLimiter, authenticateHandler);
app.get('/api/data', apiKeyAuth, dataHandler);
Secure Transmission
# Force HTTPS in nginx
server {
listen 80;
server_name api.example.com;
return 301 https://$server_name$request_uri;
}
server {
listen 443 ssl http2;
server_name api.example.com;
ssl_certificate /path/to/cert.pem;
ssl_certificate_key /path/to/key.pem;
# Only allow secure protocols
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers HIGH:!aNULL:!MD5;
location /api/ {
proxy_pass http://localhost:3000;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
}
API Key Rotation and Revocation
# Key rotation endpoint
@app.route('/api/keys/rotate', methods=['POST'])
@require_authentication
def rotate_api_key():
user = get_current_user()
old_key = user.api_key
# Generate new key
new_key = generate_secure_api_key()
# Store new key, invalidate old
store_api_key(user.id, new_key)
invalidate_api_key(old_key)
return jsonify({
'new_key': new_key,
'message': 'API key rotated successfully'
})Related CWEs: authentication
| CWE ID | Name | Severity |
|---|---|---|
| CWE-287 | Improper Authentication | CRITICAL |
| CWE-306 | Missing Authentication for Critical Function | CRITICAL |
| CWE-307 | Brute Force | HIGH |
| CWE-308 | Single-Factor Authentication | MEDIUM |
| CWE-309 | Use of Password System for Primary Authentication | MEDIUM |
| CWE-347 | Improper Verification of Cryptographic Signature | HIGH |
| CWE-384 | Session Fixation | HIGH |
| CWE-521 | Weak Password Requirements | MEDIUM |
| CWE-613 | Insufficient Session Expiration | MEDIUM |
| CWE-640 | Weak Password Recovery | HIGH |