Password Hashing ⭐
❌ Plain text, MD5, SHA-1 ✅ bcrypt (cost 12) ✅ Argon2id (modern, recommended) ✅ scrypt, PBKDF2 const hash = await bcrypt.hash(password, 12); const ok = await bcrypt.compare(password, hash);
Password Policy
- Min 8 (NIST), 12 better
- No max (allow passphrase)
- No forced complexity (counterproductive)
- Check breached (haveibeenpwned)
- Rate limit login
- Lockout after N fails
- 2FA option
Session Management
Set-Cookie: sid=abc; Secure; HttpOnly; SameSite=Lax; Max-Age=3600 session_regenerate_id(true); // on login // Invalidate server-side on logout // Idle 15-30min, absolute 24h
2FA/MFA
- SMS OTP (weak — SIM swap)
- TOTP (Google Authenticator) ✅
- WebAuthn / Passkey ✅✅ (best)
- Hardware key (YubiKey)
Secrets Management ⭐
❌ Hardcode, .env in git ✅ Env vars (basic) ✅ AWS Secrets Manager, Azure Key Vault ✅ HashiCorp Vault, GCP Secret Manager ✅ Rotation, least privilege IAM, audit
.env
# .env (NOT committed) DB_PASSWORD=secret STRIPE_KEY=sk_live_xyz # .gitignore: .env # Commit .env.example with empty values
Dependency Scan
npm audit && npm audit fix dotnet list package --vulnerable --include-transitive snyk test && snyk monitor dependency-check --scan ./
Tools: Dependabot, Renovate।
SAST (Static)
SonarQube, Snyk Code, Semgrep, CodeQL (GitHub), Checkmarx, Veracode।
DAST (Dynamic)
OWASP ZAP (free), Burp Suite, Acunetix, Nessus।
Code Review Checklist
- ☐ Inputs validated server-side
- ☐ Output encoded
- ☐ Parameterized queries
- ☐ Auth + Authorization check
- ☐ Sensitive data encrypted
- ☐ No secrets in code
- ☐ HTTPS enforced
- ☐ Security headers
- ☐ Errors don't leak info
- ☐ No sensitive data in logs
- ☐ Rate limit critical endpoints
- ☐ Dependencies updated
Input Validation
Allowlist > Blocklist, validate type/length/format/range, server-side mandatory।
Output Encoding
HTML body: < → < HTML attr: " → " URL: space → %20 JS string: " → \"