Identity & Access Management
Authentication Fundamentals
Authentication answers "who are you"; authorization (Authorization Models) answers "what can you do" — conflating the two is one of the most common access-control design mistakes. Getting authentication wrong (weak hashing, no MFA, no rate limiting) undermines every authorization decision built on top of it.
- Authentication verifies identity; authorization decides what an authenticated identity may do — treating them as one step instead of two independent decisions is a recurring design mistake
- Password hashing must use a slow, purpose-built, salted algorithm (bcrypt, scrypt, Argon2) — a fast general-purpose hash like MD5 or bare SHA-256 lets an attacker who steals the hash table brute-force billions of guesses per second on commodity GPUs
- MFA factors fall into three categories — something you know (password), something you have (phone, hardware key), something you are (biometric) — assurance goes up only when factors come from different categories, not from piling on more of the same one
- Passkeys (WebAuthn) replace a shared secret with public-key challenge-response: the private key never leaves the device, so there's no password to phish and no server-side password database to breach
- Credential stuffing replays password/username pairs leaked from one breach against every other site, banking on password reuse; defenses include rate limiting (Rate Limiting Algorithms), CAPTCHA after repeated failures, leaked-password checks at signup, and device fingerprinting
- Exponential backoff scoped to identity+IP slows a real attacker without punishing legitimate users; a hard account lockout after N failures is itself a denial-of-service vector — anyone who knows a username can lock out its owner
| Algorithm | Design goal | GPU/ASIC-resistant? | Typical use |
|---|---|---|---|
| MD5 | Fast checksumming | No — billions of hashes/sec on a GPU | Never for passwords; fine for non-security checksums |
| SHA-256 (unsalted, single pass) | Fast general-purpose hashing | No — same GPU brute-force problem | Never alone for passwords; fine as a building block inside HMAC |
| bcrypt | Deliberately slow, tunable cost factor | Partially — resists early GPUs, less so ASICs | Long-standing default for password storage |
| scrypt | Slow and memory-hard | Yes — memory cost blocks cheap parallel hardware | Password storage where memory-hardness matters most |
| Argon2 (id variant) | Winner of the Password Hashing Competition; tunable time/memory/parallelism | Yes — designed to resist GPU/ASIC/FPGA | Current best-practice default for new systems |
interface PasswordEncoder {
String encode(CharSequence rawPassword);
boolean matches(CharSequence rawPassword, String encodedPassword);
}
class LoginService {
private final PasswordEncoder encoder; // backed by BCrypt/Argon2, never a raw digest
private final UserRepository users;
boolean authenticate(String username, String rawPassword) {
User user = users.findByUsername(username).orElse(null);
if (user == null) {
encoder.matches(rawPassword, DUMMY_HASH); // do the same work for unknown users
return false;
}
return encoder.matches(rawPassword, user.getPasswordHash());
}
}