647928a0a1
Fundamentals: secure-defaults, input-validation, credential-handling, audit-logging Identity: authentication, authorization Attack Prevention: injection-prevention, dos-prevention, prompt-injection
4.1 KiB
4.1 KiB
Authentication
Rule
Verify identity before granting access. Use proven libraries, not DIY crypto.
Source: OWASP Authentication Cheat Sheet
Password Handling
Correct Pattern
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
# 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
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
# 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
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
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