Web Application Security Fundamentals #08: Authentication & Session Management
Memahami password hashing dengan Argon2id/bcrypt, secure session management, JWT security, MFA implementation, dan brute force defense.
Authentication memverifikasi siapa user. Session management memastikan siapa mereka tetap diketahui di request berikutnya. Keduanya adalah fondasi untuk authorization dan non-repudiation.
Tujuan Pembelajaran
- Memahami password storage (hashing, salt, algorithms)
- Implement secure session management
- Understand JWT (JSON Web Tokens) dan security implications
- MFA implementation dan recovery codes
- Brute force defense (rate limiting, account lockout)
Password Storage
✅ Strong Algorithms
# ✅ GOOD - Argon2id (OWASP recommended)
from argon2 import PasswordHasher
ph = PasswordHasher()
hashed = ph.hash("user_password") # Store this
# Verify:
ph.verify(hashed, "user_password") # Returns True/False
# ✅ GOOD - bcrypt
import bcrypt
salt = bcrypt.gensalt(rounds=12)
hashed = bcrypt.hashpw(b"password", salt)
# Verify:
bcrypt.checkpw(b"password", hashed)
# ❌ BAD - MD5, SHA1 (too fast, rainbow tables exist)
import hashlib
hashed = hashlib.md5(password).hexdigest() # Never do this!
Session Token Generation
# ✅ SECURE - Cryptographically random
import secrets
session_id = secrets.token_urlsafe(32) # 256-bit random
# ❌ INSECURE - Predictable
import time
session_id = str(int(time.time())) # Guessable!
Secure Session Storage
# ✅ SECURE - HttpOnly, Secure, SameSite
@app.route('/login', methods=['POST'])
def login():
user = authenticate(request.form['email'], request.form['password'])
if not user:
return 'Invalid credentials', 401
session_id = secrets.token_urlsafe(32)
# Store session server-side (database, Redis)
Session.create(session_id, user.id, expires_in=3600)
# Send HttpOnly cookie
resp = make_response({'success': True})
resp.set_cookie(
'__Host-session',
session_id,
secure=True, # HTTPS only
httpOnly=True, # JS can't access
sameSite='Lax', # CSRF protection
max_age=3600
)
return resp
JWT (JSON Web Token) Security
# ✅ SECURE JWT usage
import jwt
from datetime import datetime, timedelta
# Generate
payload = {
'user_id': 123,
'exp': datetime.utcnow() + timedelta(hours=1),
'iat': datetime.utcnow()
}
token = jwt.encode(payload, SECRET_KEY, algorithm='HS256')
# Verify
decoded = jwt.decode(token, SECRET_KEY, algorithms=['HS256'])
# If invalid or expired, exception raised
Common JWT Mistakes
# ❌ DANGEROUS - No signature verification
header, payload, sig = token.split('.')
decoded = json.loads(base64.b64decode(payload))
# Attacker can modify payload without signature check!
# ❌ DANGEROUS - Using 'none' algorithm
# JWT header: {"alg": "none"}
# Attacker send: header.payload. (no signature)
# ✅ SECURE - Strict algorithm check
jwt.decode(token, SECRET_KEY, algorithms=['HS256']) # Only HS256
Multi-Factor Authentication (MFA)
# Step 1: User login with password
@app.route('/login', methods=['POST'])
def login():
user = authenticate(email, password)
if not user:
return 'Invalid credentials', 401
# Generate MFA challenge
mfa_token = secrets.token_urlsafe(32)
cache.set(f'mfa:{mfa_token}', user.id, expire=300) # 5 min
# Send OTP
otp = ''.join(str(random.randint(0, 9)) for _ in range(6))
send_sms(user.phone, f'Your OTP: {otp}')
cache.set(f'otp:{mfa_token}', otp, expire=300)
return {'mfa_required': True, 'mfa_token': mfa_token}
# Step 2: User submit OTP
@app.route('/mfa-verify', methods=['POST'])
def mfa_verify():
mfa_token = request.json['mfa_token']
otp = request.json['otp']
user_id = cache.get(f'mfa:{mfa_token}')
correct_otp = cache.get(f'otp:{mfa_token}')
if not user_id or otp != correct_otp:
return 'Invalid OTP', 401
# Create session
session_id = secrets.token_urlsafe(32)
Session.create(session_id, user_id)
# Clean up
cache.delete(f'mfa:{mfa_token}')
cache.delete(f'otp:{mfa_token}')
return {'session': session_id}
Brute Force Defense
# ❌ VULNERABLE - No rate limiting
@app.route('/login', methods=['POST'])
def login():
user = User.find_by_email(request.form['email'])
if not user or not user.verify_password(request.form['password']):
return 'Invalid', 401
return {'session': create_session(user)}
# ✅ SECURE - Rate limiting + account lockout
from flask_limiter import Limiter
limiter = Limiter(app, key_func=lambda: request.remote_addr)
@app.route('/login', methods=['POST'])
@limiter.limit('5 per 15 minutes')
def login():
email = request.form['email']
user = User.find_by_email(email)
if user and user.locked_until and datetime.utcnow() < user.locked_until:
return 'Account locked', 429
if not user or not user.verify_password(request.form['password']):
# Increment failed attempts
if user:
user.failed_login_attempts += 1
if user.failed_login_attempts >= 5:
user.locked_until = datetime.utcnow() + timedelta(minutes=30)
user.save()
return 'Invalid', 401
# Successful login
user.failed_login_attempts = 0
user.locked_until = None
user.save()
return {'session': create_session(user)}
Session Fixation Prevention
# ❌ VULNERABLE - Same session ID after login
def login(username, password):
if verify_credentials(username, password):
return {'session': request.cookies.get('session')} # WRONG!
# ✅ SECURE - Regenerate session
def login(username, password):
if verify_credentials(username, password):
old_session_id = request.cookies.get('session')
# Delete old session
Session.delete(old_session_id)
# Create new session
new_session_id = secrets.token_urlsafe(32)
Session.create(new_session_id, user.id)
return {'session': new_session_id}
Auth Checklist
- [ ] Passwords hashed dengan Argon2id atau bcrypt
- [ ] Session ID cryptographically random (256+ bit)
- [ ] HttpOnly + Secure + SameSite cookie flags
- [ ] Session timeout (idle + absolute max)
- [ ] Rate limiting on login (5 attempts/15min)
- [ ] Account lockout setelah N failed attempts
- [ ] Session regenerated setelah login
- [ ] MFA available (TOTP preferred over SMS)
- [ ] Password reset flow aman (token dengan expiry)
- [ ] Logout invalidates session
- [ ] No credential logs atau error messages yang leak
Kesimpulan
Authentication & Session = expensive to break, cheap to implement correctly. Use proven libraries, follow best practices, test thoroughly.
Modul berikutnya (#09) membahas HTTPS, TLS, & Certificate Security.
Next: #09 - HTTPS, TLS, & Encryption
Post Terkait
Malware Analysis Fundamentals #07: Membangun Lab Analisis Malware yang Aman
Panduan membangun lab analisis malware yang aman: isolated VM, snapshot, jaringan host-only/simulasi, mematikan shared c...
Malware Analysis Fundamentals #06: Behavioral & Memory Analysis — Mengamati Perilaku Malware Secara Langsung
Tutorial behavioral & memory analysis malware: mengamati process tree, perubahan file/registry, persistence, trafik C2,...
Malware Analysis Fundamentals #05: Static & Code Analysis — Membedah Malware Tanpa Menjalankannya
Tutorial static & code analysis malware: dari hash dan strings, deteksi packer dengan entropy, sampai disassembly di Ghi...