# Authentication ## Rule Verify identity before granting access. Use proven libraries, not DIY crypto. **Source:** [OWASP Authentication Cheat Sheet](https://cheatsheetseries.owasp.org/cheatsheets/Authentication_Cheat_Sheet.html) ## Password Handling ### Correct Pattern ```python import bcrypt import secrets def hash_password(password: str) -> bytes: """Hash password using bcrypt with automatic salt.""" return bcrypt.hashpw(password.encode(), bcrypt.gensalt(rounds=12)) def verify_password(password: str, hashed: bytes) -> bool: """Verify password against hash. Constant-time comparison.""" return bcrypt.checkpw(password.encode(), hashed) # Password requirements MIN_PASSWORD_LENGTH = 12 COMMON_PASSWORDS = load_common_passwords() # Top 10k list def validate_password(password: str) -> list[str]: """Return list of validation errors.""" errors = [] if len(password) < MIN_PASSWORD_LENGTH: errors.append(f"Password must be at least {MIN_PASSWORD_LENGTH} characters") if password.lower() in COMMON_PASSWORDS: errors.append("Password is too common") return errors ``` ### Incorrect Pattern ```python # Wrong: plain text storage user.password = password # Wrong: weak hashing user.password = hashlib.md5(password.encode()).hexdigest() # Wrong: SHA without salt user.password = hashlib.sha256(password.encode()).hexdigest() # Wrong: reversible encryption user.password = encrypt(password, key) # Wrong: timing attack vulnerable if user.password == submitted_password: grant_access() ``` ## Token Management ### Correct Pattern ```python import secrets from datetime import datetime, timedelta def generate_token() -> str: """Generate cryptographically secure token.""" return secrets.token_urlsafe(32) def generate_session(user_id: str) -> dict: """Create session with expiration.""" return { "token": generate_token(), "user_id": user_id, "created_at": datetime.utcnow(), "expires_at": datetime.utcnow() + timedelta(hours=24), } def validate_session(session: dict) -> bool: """Check session validity.""" if datetime.utcnow() > session["expires_at"]: return False return True ``` ### Incorrect Pattern ```python # Wrong: predictable tokens token = f"session_{user_id}_{int(time.time())}" # Wrong: no expiration session = {"token": token, "user_id": user_id} # Wrong: client-controlled expiration if request.cookies.get("expires") > now: # User can modify! grant_access() ``` ## Multi-Factor Authentication ```python import pyotp def setup_totp(user_id: str) -> str: """Generate TOTP secret for user.""" secret = pyotp.random_base32() store_totp_secret(user_id, secret) return secret def verify_totp(user_id: str, code: str) -> bool: """Verify TOTP code with time window.""" secret = get_totp_secret(user_id) totp = pyotp.TOTP(secret) return totp.verify(code, valid_window=1) # ±30 seconds ``` ## Brute Force Protection ```python from collections import defaultdict import time class LoginRateLimiter: def __init__(self): self.attempts = defaultdict(list) self.lockouts = {} def record_attempt(self, identifier: str, success: bool): now = time.time() if not success: self.attempts[identifier].append(now) # Clean old attempts self.attempts[identifier] = [ t for t in self.attempts[identifier] if now - t < 3600 # 1 hour window ] # Lockout after 5 failures if len(self.attempts[identifier]) >= 5: self.lockouts[identifier] = now + 900 # 15 min lockout else: self.attempts[identifier] = [] self.lockouts.pop(identifier, None) def is_locked(self, identifier: str) -> bool: lockout_until = self.lockouts.get(identifier, 0) return time.time() < lockout_until ``` ## Edge Cases - Timing attacks on username enumeration - Account lockout as DOS vector - Session fixation attacks - Token leakage in logs/URLs - Password reset token reuse