Weak Authentication
Description
Weak Authentication occurs when a product implements an authentication mechanism to restrict access to specific users or identities, but fails to sufficiently verify that the claimed identity is correct. Attackers may be able to bypass weak authentication faster and/or with less effort than expected. Weak authentication can result from various issues including insufficient challenge strength, predictable authentication tokens, single-factor authentication, improper credential validation, or authentication bypass vulnerabilities.
Risk
Weak authentication has severe implications. Unauthorized access to protected resources. Identity impersonation. Privilege escalation. Data exposure. Account takeover. System compromise. Lateral movement in networks. Compliance violations. Reputational damage. High likelihood when authentication mechanisms are poorly designed or implemented.
Solution
Implement strong multi-factor authentication during architecture and design phase. Use industry-standard authentication protocols. Enforce strong password policies during implementation phase. Implement account lockout mechanisms. Use cryptographically secure session tokens. Employ proper credential storage with strong hashing. Validate all authentication inputs thoroughly.
Common Consequences
| Impact | Details |
|---|---|
| Confidentiality | Scope: Confidentiality Exposure of sensitive information to unauthorized actors through authentication bypass. |
| Integrity | Scope: Integrity Attackers may impersonate legitimate users and modify protected data. |
| Access Control | Scope: Access Control Potential execution of unauthorized operations and privilege escalation. |
Example Code
Vulnerable Code
# Vulnerable: Weak authentication implementations
import hashlib
import time
import random
# VULNERABLE: Timestamp-based authentication
class VulnerableTimestampAuth:
def authenticate(self, user_id, timestamp_token):
# VULNERABLE: Token is just current timestamp
current_time = int(time.time())
# VULNERABLE: Large time window (5 minutes)
if abs(current_time - int(timestamp_token)) < 300:
return True # Authenticated
# Attack: Attacker can predict/calculate valid tokens
return False
# VULNERABLE: Weak session token generation
def vulnerable_generate_session():
# VULNERABLE: Predictable random seed
random.seed(int(time.time()))
# VULNERABLE: Simple random number as token
return str(random.randint(0, 999999))
# VULNERABLE: MD5 password hashing (fast, vulnerable to rainbow tables)
def vulnerable_hash_password(password):
# VULNERABLE: MD5 is cryptographically broken
return hashlib.md5(password.encode()).hexdigest()
# VULNERABLE: No salt in password hash
def vulnerable_verify_password(password, stored_hash):
# VULNERABLE: Direct comparison without salt
return vulnerable_hash_password(password) == stored_hash
# VULNERABLE: Basic authentication without rate limiting
class VulnerableLoginHandler:
def __init__(self):
self.users = {} # username -> password_hash
def login(self, username, password):
# VULNERABLE: No rate limiting - allows brute force
# VULNERABLE: No account lockout
if username in self.users:
if vulnerable_verify_password(password, self.users[username]):
return vulnerable_generate_session()
return None # Login failed
// Vulnerable: Java weak authentication patterns
import java.util.Base64;
import java.security.MessageDigest;
public class VulnerableAuth {
// VULNERABLE: Basic Auth without proper validation
public boolean vulnerableBasicAuth(String authHeader) {
if (authHeader == null || !authHeader.startsWith("Basic ")) {
return false;
}
// VULNERABLE: Base64 is encoding, not encryption
String credentials = new String(
Base64.getDecoder().decode(authHeader.substring(6))
);
String[] parts = credentials.split(":");
String username = parts[0];
String password = parts[1];
// VULNERABLE: Hardcoded credentials
if ("admin".equals(username) && "admin123".equals(password)) {
return true;
}
return false;
}
// VULNERABLE: Weak password verification
public boolean vulnerablePasswordCheck(String input, String stored) {
// VULNERABLE: String comparison vulnerable to timing attacks
return input.equals(stored);
}
// VULNERABLE: Predictable session ID
public String vulnerableGenerateSession(String username) {
// VULNERABLE: Predictable session format
long timestamp = System.currentTimeMillis();
return username + "_" + timestamp;
}
// VULNERABLE: Client-side authentication reliance
public boolean vulnerableTokenAuth(String token) {
// VULNERABLE: Only checks if token exists, not validity
return token != null && token.length() > 0;
}
// VULNERABLE: Security question authentication
public boolean vulnerableSecurityQuestion(String answer, String stored) {
// VULNERABLE: Case-insensitive comparison reduces entropy
return answer.equalsIgnoreCase(stored);
// Attack: "What is your pet's name?" - guessable
}
}
// Vulnerable: JavaScript weak authentication
// VULNERABLE: Client-side authentication check
function vulnerableClientAuth(password) {
// VULNERABLE: Password checked in client-side JavaScript
const correctPassword = "secret123"; // Exposed in source
if (password === correctPassword) {
localStorage.setItem('authenticated', 'true');
return true;
}
return false;
}
// VULNERABLE: Weak JWT validation
function vulnerableJWTValidation(token) {
// VULNERABLE: No signature verification
try {
const parts = token.split('.');
const payload = JSON.parse(atob(parts[1]));
// VULNERABLE: Only checks expiration, not signature
if (payload.exp > Date.now() / 1000) {
return payload;
}
} catch (e) {
return null;
}
return null;
}
// VULNERABLE: Cookie-based auth without security flags
function vulnerableSetAuthCookie(userId) {
// VULNERABLE: Missing HttpOnly, Secure, SameSite flags
document.cookie = `auth=${userId}; path=/`;
// Attack: XSS can steal this cookie
}
// VULNERABLE: Predictable password reset token
function vulnerableGenerateResetToken(email) {
// VULNERABLE: Token based on email and time
const timestamp = Date.now();
return btoa(email + ':' + timestamp);
// Attack: Attacker can generate valid tokens
}
Fixed Code
# Fixed: Strong authentication implementations
import secrets
import hashlib
import time
from datetime import datetime, timedelta
import bcrypt
import pyotp
from collections import defaultdict
# FIXED: Strong session token generation
def safe_generate_session():
# FIXED: Cryptographically secure random token
return secrets.token_urlsafe(32)
# FIXED: Secure password hashing with bcrypt
def safe_hash_password(password):
# FIXED: bcrypt with automatic salt
salt = bcrypt.gensalt(rounds=12)
return bcrypt.hashpw(password.encode(), salt)
def safe_verify_password(password, stored_hash):
# FIXED: Constant-time comparison built into bcrypt
return bcrypt.checkpw(password.encode(), stored_hash)
# FIXED: Rate limiting and account lockout
class SafeLoginHandler:
def __init__(self):
self.users = {} # username -> password_hash
self.failed_attempts = defaultdict(list)
self.locked_accounts = {}
# FIXED: Constants for rate limiting
MAX_ATTEMPTS = 5
LOCKOUT_DURATION = timedelta(minutes=15)
ATTEMPT_WINDOW = timedelta(minutes=5)
def _is_locked(self, username):
"""FIXED: Check if account is locked."""
if username in self.locked_accounts:
lock_time = self.locked_accounts[username]
if datetime.now() < lock_time + self.LOCKOUT_DURATION:
return True
else:
del self.locked_accounts[username]
return False
def _record_failed_attempt(self, username):
"""FIXED: Record failed login attempt."""
now = datetime.now()
attempts = self.failed_attempts[username]
# Remove old attempts outside window
attempts[:] = [t for t in attempts
if now - t < self.ATTEMPT_WINDOW]
attempts.append(now)
# FIXED: Lock account after max attempts
if len(attempts) >= self.MAX_ATTEMPTS:
self.locked_accounts[username] = now
def login(self, username, password):
# FIXED: Check for account lockout
if self._is_locked(username):
raise AuthenticationError("Account locked. Try again later.")
if username not in self.users:
# FIXED: Constant time to prevent user enumeration
bcrypt.checkpw(b"dummy", bcrypt.gensalt())
self._record_failed_attempt(username)
return None
if safe_verify_password(password, self.users[username]):
# FIXED: Clear failed attempts on success
self.failed_attempts[username] = []
return safe_generate_session()
else:
self._record_failed_attempt(username)
return None
# FIXED: Multi-factor authentication
class MFAHandler:
def __init__(self):
self.user_secrets = {} # username -> TOTP secret
def setup_mfa(self, username):
"""FIXED: Generate TOTP secret for user."""
secret = pyotp.random_base32()
self.user_secrets[username] = secret
return secret
def verify_mfa(self, username, code):
"""FIXED: Verify TOTP code."""
if username not in self.user_secrets:
return False
totp = pyotp.TOTP(self.user_secrets[username])
# FIXED: Verify with small time window
return totp.verify(code, valid_window=1)
def full_login(self, username, password, mfa_code):
"""FIXED: Two-factor authentication."""
# First factor: password
if not safe_verify_password(password, self.get_password_hash(username)):
return None
# FIXED: Second factor: TOTP
if not self.verify_mfa(username, mfa_code):
return None
return safe_generate_session()
// Fixed: Java secure authentication
import java.security.SecureRandom;
import java.security.MessageDigest;
import java.util.Base64;
import java.util.Arrays;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.PBEKeySpec;
import io.jsonwebtoken.*;
public class SecureAuth {
private static final SecureRandom SECURE_RANDOM = new SecureRandom();
private static final int ITERATIONS = 100000;
private static final int KEY_LENGTH = 256;
// FIXED: Secure password hashing with PBKDF2
public String safeHashPassword(String password) throws Exception {
byte[] salt = new byte[16];
SECURE_RANDOM.nextBytes(salt);
PBEKeySpec spec = new PBEKeySpec(
password.toCharArray(), salt, ITERATIONS, KEY_LENGTH
);
SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA256");
byte[] hash = factory.generateSecret(spec).getEncoded();
// FIXED: Store salt with hash
return Base64.getEncoder().encodeToString(salt) + ":" +
Base64.getEncoder().encodeToString(hash);
}
// FIXED: Constant-time password verification
public boolean safeVerifyPassword(String password, String stored) throws Exception {
String[] parts = stored.split(":");
byte[] salt = Base64.getDecoder().decode(parts[0]);
byte[] expectedHash = Base64.getDecoder().decode(parts[1]);
PBEKeySpec spec = new PBEKeySpec(
password.toCharArray(), salt, ITERATIONS, KEY_LENGTH
);
SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA256");
byte[] actualHash = factory.generateSecret(spec).getEncoded();
// FIXED: Constant-time comparison
return MessageDigest.isEqual(expectedHash, actualHash);
}
// FIXED: Cryptographically secure session ID
public String safeGenerateSession() {
byte[] bytes = new byte[32];
SECURE_RANDOM.nextBytes(bytes);
return Base64.getUrlEncoder().withoutPadding().encodeToString(bytes);
}
// FIXED: Proper JWT validation
public Claims safeValidateJWT(String token, String secretKey) {
try {
// FIXED: Full signature verification
return Jwts.parserBuilder()
.setSigningKey(secretKey.getBytes())
.build()
.parseClaimsJws(token)
.getBody();
} catch (JwtException e) {
return null; // Invalid token
}
}
// FIXED: Secure token generation for password reset
public String safeGenerateResetToken() {
byte[] bytes = new byte[32];
SECURE_RANDOM.nextBytes(bytes);
return Base64.getUrlEncoder().withoutPadding().encodeToString(bytes);
}
}
// Fixed: JavaScript secure authentication
const crypto = require('crypto');
const jwt = require('jsonwebtoken');
// FIXED: Server-side authentication only
// Never check credentials in client-side code
// FIXED: Secure session token generation
function safeGenerateSession() {
// FIXED: Cryptographically secure random bytes
return crypto.randomBytes(32).toString('base64url');
}
// FIXED: Proper JWT validation
function safeJWTValidation(token, secretKey) {
try {
// FIXED: Full verification including signature
const payload = jwt.verify(token, secretKey, {
algorithms: ['HS256'], // FIXED: Specify algorithm
complete: true
});
return payload;
} catch (error) {
// FIXED: Log failed validation attempts
console.error('JWT validation failed:', error.message);
return null;
}
}
// FIXED: Secure cookie settings
function safeSetAuthCookie(res, token) {
res.cookie('auth', token, {
httpOnly: true, // FIXED: Not accessible to JavaScript
secure: true, // FIXED: Only sent over HTTPS
sameSite: 'strict', // FIXED: Prevent CSRF
maxAge: 3600000, // 1 hour
path: '/'
});
}
// FIXED: Cryptographically secure reset token
function safeGenerateResetToken() {
// FIXED: Unpredictable token
return crypto.randomBytes(32).toString('hex');
}
// FIXED: Rate limiting middleware
const rateLimit = require('express-rate-limit');
const loginLimiter = rateLimit({
windowMs: 15 * 60 * 1000, // 15 minutes
max: 5, // FIXED: Limit each IP to 5 requests per window
message: 'Too many login attempts, please try again later',
standardHeaders: true,
legacyHeaders: false,
});
// FIXED: Constant-time comparison for tokens
function safeCompareTokens(a, b) {
if (typeof a !== 'string' || typeof b !== 'string') {
return false;
}
// FIXED: Timing-safe comparison
return crypto.timingSafeEqual(Buffer.from(a), Buffer.from(b));
}
CVE Examples
- CVE-2024-48445: Weak timestamp-based authentication allowing authentication bypass.
- CVE-2020-8994: UART interface using empty root passwords.
- CVE-2022-29965: Deterministic utility password generation enabling credential prediction.
Related CWEs
- CWE-287: Improper Authentication (parent)
- CWE-289: Authentication Bypass by Alternate Name (child)
- CWE-290: Authentication Bypass by Spoofing (child)
- CWE-308: Use of Single-factor Authentication (child)
- CWE-309: Use of Password System for Primary Authentication (child)
References
- MITRE Corporation. "CWE-1390: Weak Authentication." https://cwe.mitre.org/data/definitions/1390.html
- OWASP. "Authentication Cheat Sheet"
- NIST SP 800-63B. "Digital Identity Guidelines: Authentication and Lifecycle Management"