Authentication โ verifying who a user is โ is one of the most critical security functions in any web application. Broken authentication is consistently a top vulnerability category in the OWASP Top 10. In this lesson, we'll explore how authentication works, how sessions are maintained, and the common pitfalls that lead to security breaches.
The authentication flow in a typical web application follows this pattern: the user provides credentials (usually username + password), the server verifies them against stored data, and if valid, creates a session that identifies the user for subsequent requests.
1. User submits credentials โ POST /login {username, password}
2. Server looks up user in database
3. Server compares password hash with stored hash
4. If match โ Server creates session, sends session cookie
5. Browser includes session cookie in all subsequent requests
6. Server validates session on each requestPasswords must NEVER be stored in plaintext. Instead, they're processed through a one-way cryptographic hash function. When a user logs in, the submitted password is hashed and compared to the stored hash.
import bcrypt
# Registration: Hash the password before storing
password = "user_password_123".encode('utf-8')
salt = bcrypt.gensalt(rounds=12)
password_hash = bcrypt.hashpw(password, salt)
# Store password_hash in database
# Login: Verify the submitted password
submitted = "user_password_123".encode('utf-8')
if bcrypt.checkpw(submitted, stored_hash):
print("Authentication successful")
else:
print("Invalid credentials")| Algorithm | Status | Why |
|---|---|---|
| MD5 | โ Broken | Fast, unsalted, rainbow table attacks |
| SHA-1 | โ Weak | Fast, designed for speed not password storage |
| SHA-256 | โ ๏ธ Insufficient | Still too fast โ GPUs can try billions/sec |
| bcrypt | โ Recommended | Adaptive cost factor, built-in salt |
| scrypt | โ Recommended | Memory-hard, resistant to GPU attacks |
| Argon2 | โ Best Choice | Winner of Password Hashing Competition |
โ ๏ธ If you discover a website storing passwords in plaintext or with MD5/SHA-1 during a security assessment, this is a CRITICAL finding. In a breach, all user credentials are immediately exposed. Always report this as the highest priority.
After authentication, the server needs to remember who the user is across multiple HTTP requests (which are stateless). This is done through sessions. The server creates a session, stores data server-side, and sends a session ID to the client as a cookie.
// Express.js session setup
const session = require('express-session');
app.use(session({
secret: process.env.SESSION_SECRET,
resave: false,
saveUninitialized: false,
cookie: {
httpOnly: true, // Prevent JavaScript access
secure: true, // HTTPS only
sameSite: 'strict', // CSRF protection
maxAge: 3600000 // 1 hour expiration
}
}));
// After successful authentication
app.post('/api/login', async (req, res) => {
const user = await authenticateUser(req.body);
if (user) {
req.session.userId = user.id;
req.session.role = user.role;
res.json({ status: 'success' });
} else {
res.status(401).json({ error: 'Invalid credentials' });
}
});JWTs are an alternative to server-side sessions. A JWT is a self-contained token that carries user data (claims) and is cryptographically signed. The server doesn't need to store session data โ it just validates the token's signature.
{
"header": {
"alg": "HS256",
"typ": "JWT"
},
"payload": {
"sub": "1234567890",
"username": "alice",
"role": "admin",
"iat": 1516239022,
"exp": 1516242622
},
"signature": "HMACSHA256(base64(header) + '.' + base64(payload), secret)"
}JWTs are encoded (Base64), NOT encrypted. Anyone can read the payload. Never put sensitive data (passwords, SSNs) in a JWT payload. The signature ensures the token hasn't been tampered with, but the contents are visible to anyone who has the token.
โ ๏ธ A common JWT vulnerability is the "alg: none" attack. If the server accepts tokens with "alg": "none", an attacker can forge tokens without knowing the secret key. Another common issue is using the public key as the HMAC secret. Always validate the algorithm on the server side.
MFA requires two or more verification factors: something you know (password), something you have (phone/token), and something you are (biometric). Even if an attacker obtains the password, they cannot authenticate without the second factor.
| Factor Type | Examples | Security Level |
|---|---|---|
| Knowledge | Password, PIN, security question | Lowest โ can be stolen/guessed |
| Possession | SMS code, TOTP app, hardware key | Medium-High โ requires physical access |
| Inherence | Fingerprint, face recognition, voice | High โ difficult to replicate |
๐ก SMS-based 2FA is better than nothing but is vulnerable to SIM swapping attacks. TOTP (Time-based One-Time Password) apps like Google Authenticator or hardware keys like YubiKey are significantly more secure. For high-security applications, FIDO2/WebAuthn is the gold standard.
Verify exercises to earn โ 140 XP and unlock next lab level.