Broken Authentication Testing: Credential Stuffing, Session Fixation, and Token Predictability
Authentication is the cornerstone of web application security — and when it breaks, the impact is almost always critical. Broken authentication vulnerabilities allow attackers to assume other users' identities, access sensitive data, and take over accounts. This guide covers the most impactful authentication testing techniques used in bug bounty programs and penetration tests.
Credential Stuffing
| Vulnerability | Test Case | Tool | Severity | Remediation |
|---|---|---|---|---|
| Weak password policy | Try password=123456 or password=username | Manual | High | Enforce complexity + length |
| No account lockout | 1000 login attempts, count successes | Hydra / Burp Intruder | High | Lockout after 5-10 failures |
| Default credentials | admin:admin, admin:password, root:root | Manual list | Critical | Change all defaults |
| JWT weak secret | Offline crack HS256 token | hashcat mode 16500 | Critical | Use RS256 with strong key |
| Password reset poisoning | Modify Host header on reset request | Manual / Burp | Critical | Validate host server-side |
| Session not invalidated on logout | Reuse old session token after logout | Manual | High | Server-side session invalidation |
| Insecure forgot password | Predictable tokens, no expiry | Manual | High | Cryptographic random tokens with TTL |
Credential stuffing uses username/password combinations from breached databases to authenticate against a target application. Unlike brute force, it relies on password reuse — a shockingly effective attack given that a significant percentage of users reuse passwords across sites.
# Using ffuf for credential stuffing:
ffuf -u https://target.com/api/auth/login -X POST -H "Content-Type: application/json" -d '{"email":"FUZZ1","password":"FUZZ2"}' -w credentials.txt:FUZZ1:FUZZ2 -fc 401 -t 10
Obtain credential lists from sites like HaveIBeenPwned, or use tools like dehashed to query breach databases for the target domain's users.
Brute Force Protection Bypass
Applications implement brute force protections at the IP level, account level, or both. Each can be bypassed:
IP Rotation Bypass
# Rotate source IPs using X-Forwarded-For header:
X-Forwarded-For: 10.0.0.1
X-Forwarded-For: 10.0.0.2
X-Forwarded-For: 10.0.0.3
# Other headers that may be trusted:
X-Real-IP: 192.168.1.1
X-Remote-IP: 172.16.0.1
X-Client-IP: 203.0.113.1
True-Client-IP: 198.51.100.1
Many applications naively trust these headers to implement rate limiting. Use Burp Intruder with a custom pitchfork attack rotating these header values alongside credential attempts.
Account Lockout Bypass
If account lockout triggers after N failed attempts, test whether logging in as a different user resets the counter. Also test whether a successful login between failed attempts resets the counter — allowing indefinite brute force with periodic valid logins interspersed.
Session Fixation
Session fixation occurs when an application assigns a session token before authentication and does not rotate it after a successful login. An attacker can set a known session token (via URL parameter, cookie injection, or cookie poisoning), convince the victim to authenticate, and then use the same known token to access the victim's authenticated session.
# Test session fixation:
# 1. Get a pre-auth session token
GET /login → Set-Cookie: session=ATTACKER_KNOWN_VALUE
# 2. If the app accepts session via URL parameter:
GET /login?sessionid=ATTACKER_KNOWN_VALUE
# 3. Victim authenticates using that token
# 4. Test if attacker's token is now authenticated:
GET /dashboard (with Cookie: session=ATTACKER_KNOWN_VALUE)
After successful login, the session token MUST change. If the pre-login and post-login tokens are identical, session fixation is confirmed.
Weak Session Token Prediction
Not all session tokens are cryptographically random. Common weaknesses:
- Sequential IDs:
session=1000,session=1001,session=1002— enumerate adjacent sessions - Base64 encoded data: Decode the token to find plaintext user data (e.g.,
user_id:1234:timestamp:1709123456) - Timestamp-based: Tokens derived from Unix timestamps can be brute-forced within a narrow window
- MD5/SHA1 of predictable inputs:
md5(username + timestamp)is trivially precomputed
# Decode and analyze session tokens:
echo "dXNlcl9pZDoxMjM0" | base64 -d
# → user_id:1234
# Check for JWT with weak secret:
# Decode at jwt.io and check the algorithm field
# Test alg:none bypass and common weak secrets (secret, password, 12345)
Remember-Me Token Attacks
Persistent "remember me" cookies often receive less security attention than session tokens. Test them for:
- Weak randomness (same predictability analysis as session tokens)
- Long expiration (10+ years is suspicious)
- Missing HttpOnly and Secure flags (stealable via XSS or network sniffing)
- Token reuse across users (generating the token from user attributes another user might share)
Request a "remember me" token for two different accounts and compare the entropy and length.
Password Reset Poisoning
Password reset flows typically generate a token-bearing link sent to the user's email. Poisoning attacks intercept or redirect this link to an attacker-controlled domain:
# Inject attacker domain via Host header:
POST /forgot-password
Host: attacker.com
Content-Type: application/x-www-form-urlencoded
[email protected]
# If the application constructs the reset URL using the Host header:
# https://attacker.com/reset-password?token=SECRET_TOKEN
# The victim receives an email with a link to attacker.com
# Attacker logs the request and captures the reset token
Also test X-Forwarded-Host, X-Host, and X-Forwarded-Server headers for the same effect. Additionally check if the reset token remains valid after use (allowing replay attacks) or has an excessively long expiration window.
Account Enumeration via Response Differences
Many login forms and password reset pages inadvertently reveal whether an account exists through different responses for valid vs. invalid usernames:
# Different response bodies:
"Invalid password" → account exists, wrong password
"Account not found" → account does not exist
# Different HTTP status codes:
200 with JSON {"error": "invalid_password"} → account exists
404 → account does not exist
# Different response times:
~200ms (password hash computed) → account exists
~5ms (early return) → account does not exist
Use Burp Intruder's response length analysis to automate account enumeration at scale.
2FA Bypass Techniques
Multi-factor authentication is often more fragile than it appears:
- Response manipulation: Intercept the failed 2FA response and change
{"success": false}to{"success": true} - Code reuse: Test if a previously used TOTP code can be replayed within the same 30-second window
- Code brute force: 6-digit TOTP codes have only 1,000,000 possibilities — test if rate limiting applies to the OTP endpoint
- Skip the step: After entering valid credentials, directly navigate to authenticated pages without completing 2FA
Testing Toolkit
Use the API Security Studio to replay authentication requests with modified parameters. Pair it with the Recon Hub to enumerate authentication-related endpoints before testing. For encoding session tokens and analyzing JWTs, use the Encoding Pipeline. For OWASP API authentication issues specifically, see our OWASP API Security Top 10 guide.
Level up your security testing
Install the CLI
npx payload-playgroundExplore All Tools
Encoding, hashing, JWT & more
Browse Cheat Sheets
Quick-reference payload guides