Replay Attack with Basic Auth
How Replay Attack Manifests in Basic Auth
Replay attacks in Basic Authentication occur when an attacker captures valid authentication credentials and resubmits them to gain unauthorized access. Since Basic Auth transmits credentials as Base64-encoded strings in the Authorization header, these credentials can be intercepted and reused if transmitted over insecure channels.
The attack typically follows this pattern: An attacker uses network sniffing tools like Wireshark or tcpdump to capture HTTP traffic containing the Authorization header. The captured header looks like: Authorization: Basic dXNlcm5hbWU6cGFzc3dvcmQ=, which decodes to username:password. The attacker can then replay this exact header to the server, bypassing authentication entirely.
Basic Auth's stateless nature makes it particularly vulnerable. Unlike session-based authentication where servers track active sessions, Basic Auth validates credentials on every request. This means a captured Authorization header remains valid until the password changes or the account is disabled. The attack can be automated using tools like Burp Suite or custom scripts that continuously replay captured headers.
Real-world examples include CVE-2021-41262 where attackers exploited Basic Auth endpoints lacking rate limiting to brute force credentials, and numerous incidents where API endpoints exposed over HTTP allowed credential capture and replay. The attack is especially effective against APIs that don't implement additional protections like API keys, tokens with expiration, or IP restrictions.
Time-based replay attacks are particularly insidious. An attacker captures credentials during one session, then replays them hours or days later when the legitimate user is offline. Without proper monitoring or anomaly detection, these attacks can go unnoticed for extended periods, especially in systems where Basic Auth is used for machine-to-machine communication or internal service authentication.
Basic Auth-Specific Detection
Detecting replay attack vulnerabilities in Basic Auth requires examining both network transmission and server-side validation patterns. The first critical check is whether authentication occurs over HTTPS rather than HTTP. Using middleBrick's CLI, you can scan your endpoints for this vulnerability:
middlebrick scan https://api.example.com/auth-endpointThe scanner examines the Authorization header transmission method and flags any endpoints accepting Basic Auth over unencrypted channels. middleBrick's black-box scanning approach means it tests the actual runtime behavior without requiring source code access.
Server-side detection involves analyzing authentication logs for unusual patterns. Look for multiple successful authentications from different IP addresses using the same credentials within short timeframes. Tools like fail2ban or custom log analyzers can flag these patterns. Additionally, check for missing rate limiting on authentication endpoints - a clear indicator that replay attacks could succeed.
Network-level detection requires monitoring for repeated identical Authorization headers. Security information and event management (SIEM) systems can be configured to alert on patterns like: same Authorization header from different source IPs, rapid succession of identical authentication attempts, or successful logins from geographically impossible locations.
middleBrick's API security scanning specifically tests for replay vulnerabilities by attempting to capture and reuse Authorization headers across different network conditions. The scanner checks if the server properly validates the request context beyond just the credentials, such as checking for request freshness, IP consistency, or implementing timestamp-based validation.
Code-level detection involves reviewing authentication middleware. Look for implementations that only check the Authorization header without additional context validation. A vulnerable implementation might look like:
def authenticate(request):
auth_header = request.headers.get('Authorization')
if not auth_header or not auth_header.startswith('Basic '):
return None
try:
credentials = base64.b64decode(auth_header[6:]).decode('utf-8')
username, password = credentials.split(':', 1)
user = User.find_by_username(username)
if user and user.check_password(password):
return user
except:
return None
return NoneThis code only validates credentials without any replay protection mechanisms, making it vulnerable to the attack described.
Basic Auth-Specific Remediation
Remediating replay attack vulnerabilities in Basic Auth requires implementing multiple defensive layers. The most fundamental fix is enforcing HTTPS/TLS for all authentication traffic. This prevents credential interception at the network level. In your web server configuration:
server {
listen 443 ssl http2;
server_name api.example.com;
ssl_certificate /path/to/cert.pem;
ssl_certificate_key /path/to/key.pem;
location /auth {
# Basic Auth configuration
auth_basic "Restricted Area";
auth_basic_user_file /etc/nginx/.htpasswd;
}
}For applications where Basic Auth must be used, implement timestamp-based nonce validation. The client includes a timestamp in the username field, and the server rejects requests older than a configurable threshold:
import base64
import datetime
from typing import Optional
def validate_basic_auth(header: str, timeout_seconds: int = 300) -> Optional[User]:
if not header or not header.startswith('Basic '):
return None
try:
credentials = base64.b64decode(header[6:]).decode('utf-8')
parts = credentials.split(':', 1)
if len(parts) != 2:
return None
username, password = parts
# Extract timestamp from username (format: username|timestamp)
try:
user_part, timestamp_str = username.rsplit('|', 1)
timestamp = datetime.datetime.fromtimestamp(float(timestamp_str))
# Check if timestamp is within valid window
if datetime.datetime.now() - timestamp > datetime.timedelta(seconds=timeout_seconds):
return None
except ValueError:
# Fallback for non-timestamp format
user_part = username
user = User.find_by_username(user_part)
if user and user.check_password(password):
return user
except Exception:
return None
return NoneImplement IP-based restrictions by maintaining a mapping of authenticated sessions to source IPs. Reject requests where the Authorization header was previously seen from a different IP:
const activeSessions = new Map();
function basicAuthMiddleware(req, res, next) {
const authHeader = req.headers.authorization;
if (!authHeader || !authHeader.startsWith('Basic ')) {
return res.status(401).send('Missing Authorization header');
}
const credentials = Buffer.from(authHeader.split(' ')[1], 'base64').toString();
const [username, password] = credentials.split(':');
// Validate credentials
const user = authenticateUser(username, password);
if (!user) {
return res.status(401).send('Invalid credentials');
}
// Check for replay from different IP
const sessionKey = `${username}:${password}`;
const lastIP = activeSessions.get(sessionKey);
if (lastIP && lastIP !== req.ip) {
console.warn(`Replay attempt detected: ${username} from ${req.ip} (last: ${lastIP})`);
return res.status(403).send('Session conflict detected');
}
activeSessions.set(sessionKey, req.ip);
req.user = user;
next();
}Rate limiting is essential for Basic Auth endpoints. Implement per-IP and per-user rate limits to prevent automated replay attacks:
from ratelimit import limits, sleep_and_retry
import time
# 5 requests per minute per IP
RATE_LIMIT = 5
TIME_PERIOD = 60
@sleep_and_retry
def rate_limited_auth_check(ip: str) -> bool:
@limits(calls=RATE_LIMIT, period=TIME_PERIOD)
def check():
return True
try:
check()
return True
except Exception:
return False
# Usage in authentication
if not rate_limited_auth_check(request.remote_addr):
return Response('Rate limit exceeded', status=429)For API services, consider migrating from Basic Auth to token-based authentication with short expiration times. JWT tokens with 5-15 minute lifetimes significantly reduce the window for successful replay attacks while maintaining usability.