Authentication Security Best Practices
Security best practices for authentication: password security, token storage, CSRF protection, MFA, and common vulnerabilities.
Password Security
Best Practices
1β
DO:
2- Use bcrypt, scrypt, or Argon2 for hashing
3- Minimum 12 characters
4- Require complexity (upper, lower, number, symbol)
5- Implement rate limiting on login
6- Use HTTPS only
7- Implement account lockout after failed attempts
8- Check against breach databases (Have I Been Pwned)
9
10β DON'T:
11- Store passwords in plain text
12- Use MD5 or SHA1 for passwords
13- Email passwords to users
14- Allow common passwords (password123, qwerty)
15- Use reversible encryption
Password Hashing Example
1import bcrypt
2
3# Hashing a password
4password = "user_password"
5salt = bcrypt.gensalt(rounds=12) # Cost factor: 12
6hashed = bcrypt.hashpw(password.encode('utf-8'), salt)
7
8# Verifying a password
9def verify_password(plain_password, hashed_password):
10 return bcrypt.checkpw(
11 plain_password.encode('utf-8'),
12 hashed_password
13 )
1// Node.js with bcrypt
2const bcrypt = require('bcrypt');
3
4// Hash password
5const saltRounds = 12;
6const hash = await bcrypt.hash(password, saltRounds);
7
8// Verify password
9const match = await bcrypt.compare(password, hash);
Password Policy Example
1import re
2
3def validate_password(password):
4 if len(password) < 12:
5 return False, "Password must be at least 12 characters"
6
7 if not re.search(r'[A-Z]', password):
8 return False, "Password must contain uppercase letter"
9
10 if not re.search(r'[a-z]', password):
11 return False, "Password must contain lowercase letter"
12
13 if not re.search(r'\d', password):
14 return False, "Password must contain number"
15
16 if not re.search(r'[!@#$%^&*(),.?":{}|<>]', password):
17 return False, "Password must contain special character"
18
19 # Check against common passwords
20 if password.lower() in COMMON_PASSWORDS:
21 return False, "Password is too common"
22
23 return True, "Password is valid"
Token Storage
Storage Options Comparison
| Location | XSS Vulnerable | CSRF Vulnerable | Best For |
|---|---|---|---|
| LocalStorage | β Yes | β No | β Avoid for auth tokens |
| SessionStorage | β Yes | β No | Temporary data only |
| Cookie (HttpOnly) | β No | β Yes | Session tokens (with CSRF protection) |
| Cookie (HttpOnly + Secure + SameSite) | β No | β No | β Best for web apps |
| Memory only | β No | β No | β SPAs (lost on refresh) |
Recommended Cookie Configuration
1Set-Cookie: token=abc123; HttpOnly; Secure; SameSite=Strict; Max-Age=3600; Path=/
Flags Explained:
HttpOnly: Prevents JavaScript access (XSS protection)Secure: Only sent over HTTPSSameSite=Strict: Prevents CSRF attacksMax-Age: Token lifetime in secondsPath=/: Cookie scope
Implementation Examples
1// Express.js (Node.js)
2res.cookie('token', jwtToken, {
3 httpOnly: true,
4 secure: process.env.NODE_ENV === 'production',
5 sameSite: 'strict',
6 maxAge: 3600000 // 1 hour in milliseconds
7});
1# Flask (Python)
2response.set_cookie(
3 'token',
4 jwt_token,
5 httponly=True,
6 secure=True,
7 samesite='Strict',
8 max_age=3600
9)
1// Go
2http.SetCookie(w, &http.Cookie{
3 Name: "token",
4 Value: jwtToken,
5 HttpOnly: true,
6 Secure: true,
7 SameSite: http.SameSiteStrictMode,
8 MaxAge: 3600,
9 Path: "/",
10})
CSRF Protection
What is CSRF?
Cross-Site Request Forgery: Attacker tricks user into making unwanted requests to a site where they're authenticated.
Attack Example
1<!-- Malicious site -->
2<img src="https://bank.com/transfer?to=attacker&amount=1000">
3<!-- Browser automatically sends cookies! -->
Protection Methods
1. CSRF Tokens (Synchronizer Token Pattern)
Implementation:
1# Flask with Flask-WTF
2from flask_wtf.csrf import CSRFProtect
3
4app = Flask(__name__)
5app.config['SECRET_KEY'] = 'your-secret-key'
6csrf = CSRFProtect(app)
7
8# In template
9<form method="POST">
10 {{ csrf_token() }}
11 <!-- form fields -->
12</form>
1// Express.js with csurf
2const csrf = require('csurf');
3const csrfProtection = csrf({ cookie: true });
4
5app.get('/form', csrfProtection, (req, res) => {
6 res.render('form', { csrfToken: req.csrfToken() });
7});
8
9app.post('/submit', csrfProtection, (req, res) => {
10 // CSRF token automatically validated
11});
2. SameSite Cookies
1Set-Cookie: session=abc; SameSite=Strict
Strict: Cookie never sent on cross-site requestsLax: Cookie sent on top-level navigation (GET only)None: Cookie sent on all requests (requires Secure flag)
3. Double Submit Cookie
11. Server sets CSRF token in cookie
22. Client reads cookie, sends in custom header
33. Server validates cookie matches header
1// Client-side
2const csrfToken = getCookie('csrf_token');
3fetch('/api/data', {
4 method: 'POST',
5 headers: {
6 'X-CSRF-Token': csrfToken
7 },
8 body: JSON.stringify(data)
9});
4. Custom Headers (for APIs)
1// CORS prevents cross-origin custom headers
2fetch('/api/data', {
3 method: 'POST',
4 headers: {
5 'X-Requested-With': 'XMLHttpRequest'
6 }
7});
Multi-Factor Authentication (MFA)
Authentication Factors
- Something you know: Password, PIN
- Something you have: Phone, hardware token, authenticator app
- Something you are: Fingerprint, face recognition
TOTP (Time-based One-Time Password)
1Shared Secret β HMAC-SHA1(Secret, Time) β 6-digit code
Setup Flow
Login Flow with MFA
Implementation Example
1import pyotp
2import qrcode
3
4# Generate secret
5secret = pyotp.random_base32()
6
7# Generate QR code
8totp_uri = pyotp.totp.TOTP(secret).provisioning_uri(
9 name='user@example.com',
10 issuer_name='MyApp'
11)
12qrcode.make(totp_uri).save('qr_code.png')
13
14# Verify code
15def verify_totp(secret, code):
16 totp = pyotp.TOTP(secret)
17 return totp.verify(code, valid_window=1) # Allow 30s window
1// Node.js with speakeasy
2const speakeasy = require('speakeasy');
3const QRCode = require('qrcode');
4
5// Generate secret
6const secret = speakeasy.generateSecret({
7 name: 'MyApp (user@example.com)'
8});
9
10// Generate QR code
11QRCode.toDataURL(secret.otpauth_url, (err, dataUrl) => {
12 // Display dataUrl as image
13});
14
15// Verify code
16const verified = speakeasy.totp.verify({
17 secret: secret.base32,
18 encoding: 'base32',
19 token: userCode,
20 window: 1
21});
Backup Codes
1import secrets
2
3def generate_backup_codes(count=10):
4 codes = []
5 for _ in range(count):
6 code = '-'.join([
7 secrets.token_hex(2).upper()
8 for _ in range(3)
9 ])
10 codes.append(code)
11 return codes
12
13# Example output: ['A3-F7-2B', 'D9-1C-8E', ...]
14
15# Store hashed
16import bcrypt
17hashed_codes = [bcrypt.hashpw(code.encode(), bcrypt.gensalt())
18 for code in codes]
Token Refresh Pattern
Benefits
- Short-lived access tokens limit damage if stolen
- Can revoke refresh tokens server-side
- Balance security and UX
Storage Strategy
- Access Token: Memory (SPA) or HttpOnly cookie
- Refresh Token: HttpOnly, Secure, SameSite cookie (longer TTL)
Implementation Example
1// Client-side token refresh
2async function fetchWithAuth(url, options = {}) {
3 let response = await fetch(url, {
4 ...options,
5 headers: {
6 ...options.headers,
7 'Authorization': `Bearer ${accessToken}`
8 }
9 });
10
11 if (response.status === 401) {
12 // Try to refresh token
13 const refreshed = await refreshAccessToken();
14 if (refreshed) {
15 // Retry original request
16 response = await fetch(url, {
17 ...options,
18 headers: {
19 ...options.headers,
20 'Authorization': `Bearer ${accessToken}`
21 }
22 });
23 } else {
24 // Redirect to login
25 window.location.href = '/login';
26 }
27 }
28
29 return response;
30}
31
32async function refreshAccessToken() {
33 const response = await fetch('/api/refresh', {
34 method: 'POST',
35 credentials: 'include' // Send refresh token cookie
36 });
37
38 if (response.ok) {
39 const data = await response.json();
40 accessToken = data.access_token;
41 return true;
42 }
43 return false;
44}
Common Vulnerabilities
1. JWT Vulnerabilities
Algorithm Confusion Attack:
1// Attacker changes header
2{
3 "alg": "none", // Changed from "RS256"
4 "typ": "JWT"
5}
Mitigation:
1# Always specify and validate algorithm
2import jwt
3
4def verify_token(token):
5 try:
6 payload = jwt.decode(
7 token,
8 public_key,
9 algorithms=['RS256'] # Whitelist specific algorithm
10 )
11 return payload
12 except jwt.InvalidAlgorithmError:
13 return None
Other JWT Issues:
1β Weak signing keys (< 256 bits)
2β No expiration validation
3β Storing sensitive data in payload (it's base64, not encrypted!)
4β Not validating issuer (iss) and audience (aud)
Best Practices:
1# Generate strong key
2import secrets
3secret_key = secrets.token_urlsafe(32) # 256 bits
4
5# Create JWT with all claims
6token = jwt.encode({
7 'sub': user_id,
8 'iat': datetime.utcnow(),
9 'exp': datetime.utcnow() + timedelta(minutes=15),
10 'iss': 'myapp.com',
11 'aud': 'myapp.com'
12}, secret_key, algorithm='HS256')
13
14# Verify with all checks
15payload = jwt.decode(
16 token,
17 secret_key,
18 algorithms=['HS256'],
19 issuer='myapp.com',
20 audience='myapp.com'
21)
2. Broken Access Control (IDOR)
Insecure Direct Object References:
1β BAD: GET /api/users/123/orders
2β Attacker changes to /api/users/456/orders
Mitigation:
1@app.route('/api/users/<user_id>/orders')
2@login_required
3def get_orders(user_id):
4 # Always check authorization!
5 if current_user.id != user_id and not current_user.is_admin:
6 abort(403)
7
8 return Orders.query.filter_by(user_id=user_id).all()
Better: Use UUIDs instead of sequential IDs:
1import uuid
2
3# Generate UUID
4user_id = str(uuid.uuid4()) # e.g., '550e8400-e29b-41d4-a716-446655440000'
3. Session Fixation
Attack: Attacker sets victim's session ID before login
Mitigation:
1# Regenerate session ID after login
2@app.route('/login', methods=['POST'])
3def login():
4 user = authenticate(request.form['username'], request.form['password'])
5 if user:
6 # Regenerate session ID
7 session.regenerate()
8 session['user_id'] = user.id
9 return redirect('/dashboard')
Security Checklist
1β
Passwords hashed with bcrypt/Argon2 (cost factor β₯ 12)
2β
HTTPS enforced (HSTS header)
3β
Rate limiting on login/API endpoints
4β
Account lockout after failed attempts
5β
CSRF protection enabled
6β
Secure cookie flags (HttpOnly, Secure, SameSite)
7β
JWT with short expiration (15 min) + refresh tokens
8β
MFA available for sensitive operations
9β
Authorization checks on every endpoint
10β
Input validation and sanitization
11β
Security headers (CSP, X-Frame-Options, etc.)
12β
Regular security audits and penetration testing
13β
Logging and monitoring for suspicious activity
Related Snippets
- Authentication & Authorization Fundamentals
Core authentication and authorization concepts overview. This page provides a β¦ - Authentication Methods
Comprehensive guide to authentication methods: sessions, JWT, OAuth 2.0, OIDC, β¦ - Authorization Models
Authorization models: RBAC, ABAC, and ACL with practical examples. 1. Role-Based β¦