A02 Cryptographic Failures
A02:2021 — Cryptographic Failures — covers data not protected in transit, weak crypto, hard-coded keys, predictable randomness, broken authentication. The fix is “use the standard libraries the right way.”
TLS, hashing, key management, randomness
EXAMPLE
// 1) HTTPS / TLS — non-negotiable
# nginx — modern config
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers 'ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256';
ssl_prefer_server_ciphers on;
ssl_session_cache shared:SSL:50m;
ssl_session_timeout 4h;
add_header Strict-Transport-Security 'max-age=31536000; includeSubDomains; preload' always;
// 2) Hashing passwords — use a slow KDF (argon2id, scrypt, bcrypt)
// Node
import argon2 from 'argon2';
const hash = await argon2.hash(password, {
type: argon2.argon2id, memoryCost: 19*1024, timeCost: 2, parallelism: 1,
});
await argon2.verify(stored, attempt);
// NEVER:
// sha256(password) — fast hash, GPU-bruteforce in seconds
// md5(password) — broken on every axis
// sha256(password + salt) — still too fast
// 3) Hashing data integrity — SHA-256 / SHA-3 / BLAKE3
import crypto from 'node:crypto';
const digest = crypto.createHash('sha256').update(data).digest('hex');
// 4) Authenticated encryption — AES-GCM or ChaCha20-Poly1305
const key = crypto.randomBytes(32);
const iv = crypto.randomBytes(12);
const cipher = crypto.createCipheriv('aes-256-gcm', key, iv);
const ct = Buffer.concat([cipher.update(plaintext), cipher.final()]);
const tag = cipher.getAuthTag();
// Pack iv || ct || tag
// NEVER:
// aes-256-cbc + your-own-HMAC — easy to mess up
// aes-256-ecb — leaks structure (the famous penguin)
// 5) Random — only from a CSPRNG
const token = crypto.randomBytes(32).toString('base64url'); // OK
const uuid = crypto.randomUUID(); // OK
// NEVER: Math.random() for tokens / IDs / nonces / keys
// 6) Signing — HMAC for symmetric, RSA / ECDSA / Ed25519 for asymmetric
const sig = crypto.createHmac('sha256', key).update(message).digest('hex');
// Verify with constant-time compare:
crypto.timingSafeEqual(Buffer.from(sig), Buffer.from(expected));
// JWTs — RS256 / ES256 / EdDSA. NEVER use 'none' or HS256 with a guessable secret.
// 7) Key management — the actual hard part
# • Generate keys with a CSPRNG
# • Store in a KMS / Secrets Manager (AWS KMS, GCP KMS, Vault, Doppler)
# • NEVER commit keys to git
# • Rotate periodically; envelope encryption (KEK / DEK)
# • Use service identities (IAM roles) — never long-lived API keys in env files
// 8) PII at rest — encrypt sensitive fields
# Database-level: TDE (transparent disk encryption)
# Field-level: encrypt(name, ssn, dob) with a per-tenant key
# Hash for search: HMAC(value, server_secret) — collisions stay deterministic
// 9) Avoid common mistakes
// Hard-coded API keys in source
// .env files committed to git
// Secrets in logs / error messages / Sentry breadcrumbs
// Cookies without Secure on HTTPS sites
// Token comparison with `===` instead of timingSafeEqual
// Building your own crypto primitives — use libsodium / @noble/* / vetted libraries
// 10) Compliance + standards
# • OWASP ASVS level 2 covers crypto in detail
# • NIST SP 800-63B for password storage
# • PCI-DSS for cardholder data (encryption + tokenisation)
# • GDPR / Australia Privacy Act — encryption is the de-facto control for PII
// 11) Audit checklist
# - HTTPS-only with HSTS preload
# - Modern TLS suites only (TLS 1.2/1.3)
# - Passwords: argon2id / scrypt / bcrypt
# - PII / payment data encrypted at rest with KMS-managed keys
# - Tokens / sessions use crypto.randomBytes (not Math.random)
# - No hard-coded secrets in source (gitleaks / trufflehog scan in CI)
# - JWT signing keys are >= 256-bit, rotated, and stored in KMS
# - Every secret has an owner + rotation schedule
// 12) Tools
# • SSL Labs (https://www.ssllabs.com/ssltest/) — grade your HTTPS
# • Mozilla Observatory — overall security headers
# • testssl.sh — local TLS scanner
# • gitleaks / trufflehog — scan repos for committed secrets
# • OWASP ASVS checklist — formal coverage
Why it matters
Most crypto failures are mundane: weak hash on passwords, plaintext PII in the DB, secrets in git, Math.random() for tokens. Use vetted libraries with safe defaults; the “clever” crypto code is almost always the wrong move.
Tip: Tweak the snippet with Try it Yourself », then sit the quiz at the bottom of the page.
Example
Example
// A02 Cryptographic Failures — sensitive data at rest / in transit. // Examples: HTTP for login, MD5 passwords, weak random tokens, hard-coded keys. // Fix: TLS everywhere, argon2id for passwords, libsodium for crypto, KMS for keys.Try it Yourself »
Discussion
Loading…