Working with Password-Based Authentication

1. Implementing Username-Password Login

StepBest Practice
TransportHTTPS only, HSTS enabled
LookupCase-insensitive username, constant-time
Verifybcrypt/argon2 compare (never plain ==)
ResponseGeneric error to prevent enumeration
Post-loginRotate session ID, set Secure cookie

Example: Java Spring Boot login

@PostMapping("/login")
public ResponseEntity<?> login(@RequestBody LoginRequest req) {
  User user = userRepo.findByEmailIgnoreCase(req.email())
      .orElseThrow(() -> new BadCredentialsException("Invalid credentials"));
  if (!passwordEncoder.matches(req.password(), user.passwordHash())) {
    throw new BadCredentialsException("Invalid credentials");
  }
  String token = jwtService.issue(user);
  return ResponseEntity.ok(new TokenResponse(token));
}

2. Hashing Passwords

AlgorithmRecommended Params (2026)Notes
Argon2id PREFERREDm=64MB, t=3, p=1OWASP recommended winner
bcryptcost=12 (min)Widely supported, 72-byte limit
scryptN=2^17, r=8, p=1Memory-hard
PBKDF2-HMAC-SHA256≥600,000 iterationsFIPS compliant
MD5 / SHA1 / SHA256 DEPRECATEDFast = crackable

Example: Argon2id in Node.js

import argon2 from "argon2";

const hash = await argon2.hash(password, {
  type: argon2.argon2id,
  memoryCost: 65536,   // 64 MB
  timeCost: 3,
  parallelism: 1
});

const ok = await argon2.verify(hash, password);

3. Salting Passwords

PropertyRequirement
UniquenessPer-user, never reused
Length≥16 bytes (128 bits)
SourceCSPRNG (crypto.randomBytes)
StorageEmbedded in hash string (bcrypt/argon2 do this)
PepperApp-wide secret added to password (kept in HSM/vault)

4. Setting Password Complexity Requirements

Requirement (NIST SP 800-63B)Recommendation
Minimum length≥8 chars (12+ preferred)
Maximum length≥64 chars allowed
Allowed charsAll Unicode + spaces
Composition rulesNOT required (no forced symbols)
Breach checkReject if in HIBP/breach list
Periodic changeNOT required unless compromise
Warning: NIST 2017+ guidance deprecated forced complexity rules and periodic resets — they encourage weaker passwords.

5. Implementing Password Strength Validation

Example: zxcvbn entropy check

import zxcvbn from "zxcvbn";
const result = zxcvbn(password, [email, username]);
if (result.score < 3) {
  throw new Error(`Weak password: ${result.feedback.suggestions.join(" ")}`);
}
// score: 0 (too guessable) → 4 (very strong)
ScoreCrack TimeVerdict
0< 10² guessesReject
110⁶Reject
210⁸Weak
310¹⁰Acceptable
4≥10¹⁰Strong

6. Preventing Password Enumeration Attacks

SurfaceMitigation
LoginGeneric "Invalid email or password"
RegistrationSend confirmation email regardless of existence
Password resetAlways respond "If account exists, email sent"
TimingAlways compute hash even for unknown users
Rate limitingPer IP + per identifier

7. Implementing Account Lockout Policies

ParameterTypical Value
Threshold5–10 failed attempts
Window15 minutes
Lockout duration15–30 min (exponential backoff)
ScopePer account + per IP
UnlockTimer expiry or email link

8. Handling Password Reset Flow

Password Reset Process

  1. User requests reset (POST /forgot-password with email)
  2. Server generates cryptographically-random token (32 bytes)
  3. Hash token, store with userId + expiry (15 min)
  4. Email plaintext token in single-use link
  5. User clicks link → verifies token hash + expiry
  6. User submits new password → hash + store, invalidate token
  7. Invalidate all existing sessions for the user
  8. Send confirmation email

9. Implementing Secure Password Recovery

ControlImplementation
Token entropy≥128 bits (CSPRNG)
StorageSHA-256 hashed in DB
Expiration≤15 minutes
Single useDelete on first use
TransportHTTPS link only
Session invalidationRevoke all sessions after reset

10. Using Password Breach Detection

Example: HIBP k-anonymity check

import { createHash } from "crypto";

async function isPwned(password) {
  const sha1 = createHash("sha1").update(password).digest("hex").toUpperCase();
  const prefix = sha1.slice(0, 5);
  const suffix = sha1.slice(5);
  const res = await fetch(`https://api.pwnedpasswords.com/range/${prefix}`);
  const body = await res.text();
  return body.split("\n").some(line => line.startsWith(suffix));
}
ServiceUse
HaveIBeenPwnedK-anonymous SHA-1 prefix API (free)
EnzoicCommercial breach + dark-web monitoring
SpyCloudEnterprise credential exposure

11. Enforcing Password Expiration Policies

TriggerAction
Suspected compromiseForce reset immediately
Breach detectedInvalidate + email user
RegulatoryPeriodic only if mandated (PCI DSS 4.0: optional with MFA)
High-privilege accounts90-day rotation if no MFA

12. Preventing Password Reuse

StrategyDetail
History tableStore last N=5–10 password hashes
ComparisonVerify against each historical hash (Argon2 verify)
Cross-siteBlock known-breached passwords (HIBP)
SimilarityReject Levenshtein distance < 3 from previous