Back to Cheat Sheets

🔒 Use Cryptography to Protect Data

OWASP Top 10 Proactive Controls (2024) - C2

CORE CONTROL

📋 What Is This Control?

Use Cryptography to Protect Data means applying correct, current, well-implemented cryptography wherever sensitive data lives or travels - so even if an attacker reaches the bytes, they cannot read, forge, or tamper with them. It covers data in transit (TLS 1.2+/1.3), at rest (authenticated encryption like AES-256-GCM), and in use. The control moves the security boundary from "protect all the data" to "protect the key" - a smaller, more defensible thing. It is the defense against the Cryptographic Failures risk (A02).

C2 Proactive Control
Confidentiality Focus Area
OWASP 2024 Proactive Controls

🎯 What It Prevents

  • Plaintext/Weak Password Stores: Bulk cracking of MD5/SHA-1 or unsalted hashes
  • Unencrypted Data at Rest: A leaked backup or bucket exposing every record
  • Cleartext Transport: On-path interception of credentials and cookies
  • TLS Downgrade: Stripping encryption via missing HSTS / weak ciphers
  • Tampering / Forgery: Malleable ciphertext without authenticated encryption
  • Predictable Tokens: Guessable session IDs and reset links from weak RNGs
  • Hardcoded / Leaked Keys: Secrets committed to source or baked into images

🧭 How It Works

1. Classify data - mark what is restricted / confidential
↓
2. Encrypt in transit - TLS 1.2+/1.3 with HSTS, no downgrade
↓
3. Encrypt at rest - AEAD (AES-256-GCM), encrypted backups
↓
4. Hash passwords with a slow, salted function; CSPRNG for all tokens
↓
5. Manage keys in a KMS/HSM - rotate, separate, never hardcode

❌ Without This Control

# Bad: MD5, unsalted - cracked in seconds, timing-unsafe compare import hashlib def store_password(pw: str) -> str: return hashlib.md5(pw.encode()).hexdigest() # Bad: ECB mode + hardcoded key - leaks patterns, no integrity from Crypto.Cipher import AES KEY = b"1234567890123456" ← hardcoded, committed to the repo def encrypt(data: bytes) -> bytes: cipher = AES.new(KEY, AES.MODE_ECB) ← ECB leaks plaintext patterns pad = 16 - len(data) % 16 return cipher.encrypt(data + bytes([pad]) * pad) # Bad: predictable, time-seeded token - guessable import random, time def reset_token() -> str: random.seed(int(time.time())) ← predictable seed return "".join(random.choice("0123456789abcdef") for _ in range(32))

✅ Applying the Control

# Good: Argon2id - slow, memory-hard, salt embedded, constant-time verify from argon2 import PasswordHasher ph = PasswordHasher(time_cost=3, memory_cost=64 * 1024, parallelism=1) def store_password(pw: str) -> str: return ph.hash(pw) # store this string; contains algo, params, salt # Good: AES-256-GCM (AEAD) - confidential + tamper-evident; key from KMS import os from cryptography.hazmat.primitives.ciphers.aead import AESGCM def encrypt(data: bytes, key: bytes, aad: bytes = b"") -> bytes: nonce = os.urandom(12) # CSPRNG; unique per message return nonce + AESGCM(key).encrypt(nonce, data, aad) def decrypt(blob: bytes, key: bytes, aad: bytes = b"") -> bytes: nonce, ct = blob[:12], blob[12:] return AESGCM(key).decrypt(nonce, ct, aad) # raises on tamper - fail closed # Good: CSPRNG token + constant-time comparison import secrets, hmac def reset_token() -> str: return secrets.token_urlsafe(32) # ~256 bits of entropy def token_matches(a: str, b: str) -> bool: return hmac.compare_digest(a, b) # nginx: TLS 1.2/1.3 only, HSTS, HTTP → HTTPS redirect ssl_protocols TLSv1.2 TLSv1.3; add_header Strict-Transport-Security "max-age=31536000; includeSubDomains; preload" always;

✓ Implementation Checklist

  • Sensitive data is classified, and the policy drives encryption decisions
  • All traffic uses TLS 1.2+/1.3 with HSTS; weak protocols/ciphers disabled
  • Data at rest uses AES-256-GCM (or ChaCha20-Poly1305); backups encrypted
  • Passwords use Argon2id/bcrypt/scrypt/PBKDF2 with per-user salt
  • All keys, salts, nonces, and tokens come from a CSPRNG
  • Nonces/IVs are unique per encryption; no (key, nonce) reuse
  • Keys live in a KMS/HSM, separated from data, rotated, never hardcoded
  • Only vetted libraries are used; no home-grown crypto
  • Ciphertext/hashes are versioned for crypto-agility
  • Secrets never appear in logs, URLs, or caches; CI blocks committed secrets

🔍 Tools & Techniques

Libraries & Key Management:

libsodium / PyNaCl Google Tink argon2 bcrypt AWS KMS HashiCorp Vault HSM

Verification & Scanning:

testssl.sh SSL Labs gitleaks semgrep (p/crypto) pip-audit / npm audit

How to Verify:

  • Scan TLS: confirm 1.2+/1.3 only, HSTS present, weak ciphers disabled
  • Grep the codebase for MD5/SHA-1 password use, DES/RC4/ECB, Math.random
  • Confirm no hardcoded keys/secrets committed (secret scanning in CI)
  • Verify a fresh nonce is generated per encryption
  • Check backups and buckets are encrypted at rest

🌍 Why It Matters

  • Plaintext or weakly hashed passwords: A single leak lets attackers recover most passwords with rainbow tables and GPU cracking, then replay them (credential stuffing). Per-user salted Argon2id/bcrypt makes bulk cracking infeasible.
  • Unencrypted data at rest: One leaked backup, stolen disk, or misconfigured bucket exposes every record. Field encryption plus encrypted volumes turns the same leak into unreadable ciphertext.
  • Missing or downgradable transport encryption: An on-path attacker strips TLS and reads cookies and credentials. TLS 1.2+/1.3 only with HSTS closes the window.
  • Hardcoded and leaked keys: Anyone with repo or image access can decrypt data or forge tokens. Keys in a KMS/secrets manager, injected at runtime and rotated, remove the class.

These are documented cryptographic-failure classes, described as patterns - no fabricated breach figures. Many regulations offer safe-harbour relief when data was strongly encrypted.

📌 Quick Tips

  • DO NOT hash passwords with MD5/SHA-1 or a single fast hash
  • DO NOT use ECB mode, reuse a nonce, or hardcode a key
  • DO NOT roll your own crypto or confuse encoding with encryption
  • DO use authenticated encryption (AES-256-GCM/ChaCha20-Poly1305)
  • DO hash passwords with Argon2id/bcrypt and per-user salt
  • DO source all keys, salts, nonces, and tokens from a CSPRNG
  • DO manage keys in a KMS/HSM and stay crypto-agile