Rainbow Table Attack with Basic Auth
How Rainbow Table Attack Manifests in Basic Auth
Rainbow table attacks exploit the fundamental weakness of Basic Authentication: credentials are transmitted as Base64-encoded strings, and if the server stores passwords in plaintext or unsalted format, attackers can precompute hash tables to reverse them quickly.
The attack pattern works like this: an attacker captures Basic Auth credentials (username:password encoded as Base64) from network traffic, logs, or exposed endpoints. Since Basic Auth sends credentials with every request, they appear in server logs, proxy logs, and can be intercepted on unencrypted connections. The Base64 encoding is trivially reversible, giving attackers the exact username and password combination.
Once the attacker has the password, they check if it's stored using weak hashing. If the server uses unsalted MD5, SHA1, or even unsalted SHA256, precomputed rainbow tables can reverse common passwords in seconds. For example, the password "password123" always produces the same MD5 hash (482c811da5d5b4bc6d497ffa98491e38), so attackers can look up this hash in massive precomputed tables.
Basic Auth's stateless nature amplifies this risk. Since credentials are sent with every request, they're more likely to be logged or cached. Many applications log the full Authorization header for debugging, inadvertently storing Base64-encoded credentials in log files. These logs become treasure troves for attackers who gain file system access.
The attack also exploits weak password policies. Basic Auth implementations often lack rate limiting or account lockout, allowing attackers to brute force weak passwords without triggering defenses. Combined with rainbow tables, this creates a devastating attack vector where common passwords are instantly compromised.
Real-world examples include CVE-2020-12273 (D-Link routers) where weak Basic Auth implementations allowed credential harvesting, and countless IoT devices that use hardcoded Basic Auth credentials vulnerable to rainbow table attacks.
Basic Auth-Specific Detection
Detecting rainbow table vulnerabilities in Basic Auth requires examining both the authentication mechanism and password storage. Start by analyzing how credentials are handled on the server side.
First, check if the server logs Authorization headers. This is a critical vulnerability because it stores Base64-encoded credentials in plaintext. Look for middleware that logs request headers or inspect log files for patterns like "Authorization: Basic dXNlcm5hbWU6cGFzc3dvcmQ=".
Next, examine the password hashing implementation. Basic Auth implementations vulnerable to rainbow tables typically use:
- Plaintext password storage
- Unsalted MD5 or SHA1 hashes
- Static salts (same salt for all users)
- No key stretching (PBKDF2, bcrypt, scrypt, argon2)
Use tools like hash-identifier to analyze stored password hashes. If you see identical hashes for multiple users with the same password, that's unsalted hashing—a clear indicator of vulnerability.
middleBrick's scanner specifically detects these Basic Auth weaknesses through its Authentication check category. The scanner examines the runtime behavior of Basic Auth endpoints, checking for:
- Weak password policies (short passwords, common passwords)
- Missing rate limiting on authentication endpoints
- Log exposure of Authorization headers
- Server-side password storage analysis via timing attacks
The scanner also tests for common Basic Auth misconfigurations like default credentials, weak credential exposure through error messages, and missing secure flag on authentication cookies (if used alongside Basic Auth).
For manual testing, use curl to test authentication endpoints and observe response patterns. Time-based analysis can reveal unsalted hashing—if the response time is constant regardless of password length, it suggests precomputed hash lookup rather than actual password verification.
Basic Auth-Specific Remediation
Remediating rainbow table vulnerabilities in Basic Auth requires both secure credential transmission and robust password storage. Here's how to implement proper security using Basic Auth's native features.
First, always use HTTPS/TLS. Basic Auth credentials are only secure when transmitted over encrypted channels. Without TLS, credentials are exposed in plaintext to anyone monitoring network traffic. Configure your web server to enforce HTTPS and redirect HTTP to HTTPS.
For password storage, replace any unsalted hashing with modern key derivation functions:
import bcrypt
import os
def hash_password(password: str) -> bytes:
# Generate a unique salt for each password
salt = bcrypt.gensalt()
# Hash with bcrypt (includes salt automatically)
hashed = bcrypt.hashpw(password.encode('utf-8'), salt)
return hashed
def verify_password(stored_hash: bytes, password: str) -> bool:
return bcrypt.checkpw(password.encode('utf-8'), stored_hash)
# Usage
stored = hash_password("correct-horse-battery-staple")
if verify_password(stored, "correct-horse-battery-staple"):
print("Authentication successful")
This Python example uses bcrypt, which automatically handles salting and key stretching. The stored hash includes the salt, making rainbow tables ineffective since each password has a unique salt.
For Node.js/Express applications, implement Basic Auth with proper password handling:
const express = require('express');
const bcrypt = require('bcrypt');
const app = express();
// Middleware to parse Basic Auth headers
function basicAuthMiddleware(req, res, next) {
const authHeader = req.headers.authorization;
if (!authHeader || !authHeader.startsWith('Basic ')) {
return res.status(401).setHeader('WWW-Authenticate', 'Basic').end();
}
// Decode Base64 credentials
const base64Credentials = authHeader.split(' ')[1];
const [username, password] = Buffer.from(base64Credentials, 'base64')
.toString('ascii').split(':');
// Verify credentials against database
const user = getUserFromDatabase(username);
if (!user || !bcrypt.compareSync(password, user.passwordHash)) {
return res.status(401).setHeader('WWW-Authenticate', 'Basic').end();
}
req.user = user;
next();
}
// Protected route
app.get('/api/protected', basicAuthMiddleware, (req, res) => {
res.json({ message: 'Access granted', user: req.user.username });
});
app.listen(3000);
This implementation ensures credentials are only decoded server-side, uses bcrypt for secure password verification, and returns proper 401 responses without revealing information.
Additional protections include:
- Implement rate limiting on authentication endpoints to prevent brute force attacks
- Use account lockout after multiple failed attempts
- Enforce strong password policies (minimum 12 characters, complexity requirements)
- Log authentication failures without logging successful credentials
- Consider implementing multi-factor authentication for sensitive endpoints
For legacy systems that must maintain Basic Auth compatibility, use a reverse proxy with authentication and connect to backend services using token-based authentication instead.