HIGH broken authenticationapi keys

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 -10

Api 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 IDNameSeverity
CWE-287Improper Authentication CRITICAL
CWE-306Missing Authentication for Critical Function CRITICAL
CWE-307Brute Force HIGH
CWE-308Single-Factor Authentication MEDIUM
CWE-309Use of Password System for Primary Authentication MEDIUM
CWE-347Improper Verification of Cryptographic Signature HIGH
CWE-384Session Fixation HIGH
CWE-521Weak Password Requirements MEDIUM
CWE-613Insufficient Session Expiration MEDIUM
CWE-640Weak Password Recovery HIGH

Frequently Asked Questions

How can I tell if my API keys are being leaked?
Look for API keys in client-side JavaScript, commit history, or error responses. Use middleBrick's scanning to detect exposed keys in your API responses and headers. Check for keys in logs, error messages, and stack traces that might be returned to clients.
What's the difference between API key authentication and JWT authentication?
API keys are static credentials that identify the client but don't carry user context. JWTs are signed tokens that can contain user information, expiration, and permissions. API keys are simpler but less flexible - they're good for service-to-service authentication, while JWTs excel at user authentication with granular permissions.