Cryptographic Failures occur when an application protects sensitive data with weak, misconfigured, or entirely absent cryptography, and the category sits at number two on the OWASP Top 10 (2021), directly behind Broken Access Control. OWASP renamed it from the 2017 "Sensitive Data Exposure" deliberately: exposure is the symptom, but the root cause is almost always a cryptographic mistake — a hardcoded key, an unsalted hash, a deprecated cipher, or plaintext sent over the wire. This guide covers what qualifies under the category, why it ranks so high, the real incidents that illustrate it, and how engineering teams detect and remediate it before shipping.
What OWASP A02 actually covers
A02:2021 maps to 29 CWEs — more than any category except Injection — spanning the entire lifecycle of data protection: what gets encrypted, how, where the keys live, and whether transport encryption is truly enforced end to end. The defining weaknesses include CWE-327 (use of a broken or risky algorithm), CWE-759 and CWE-760 (unsalted or predictable-salt hashing), CWE-798 (hardcoded credentials), CWE-331 (insufficient entropy), and CWE-320 (key management errors). Unlike Injection, which describes one attack pattern, Cryptographic Failures is a category of design and configuration mistakes. The first question OWASP tells you to ask is whether data is sensitive at all — passwords, tokens, health records, payment data — and then whether it is protected both in transit and at rest with modern primitives.
Why it ranks number two
The category ranks second because the blast radius is enormous and the exploitation cost is low. When cryptography fails, an attacker who gains any read access — a stolen backup, a misconfigured bucket, intercepted traffic — inherits the full value of the data without ever touching application logic. OWASP's 2021 data set recorded a high average exploitability score for the category and thousands of associated CVEs. Weak password hashing is especially damaging because it is retroactive: a leaked database of unsalted MD5 or SHA-1 hashes is crackable on consumer GPUs at billions of guesses per second, so a breach discovered years later still yields plaintext passwords. The 2012 LinkedIn incident is the enduring proof — 6.5 million unsalted SHA-1 hashes leaked, and a much larger 117 million-credential set from the same breach surfaced for sale four years later.
Real-world examples
Heartbleed (CVE-2014-0160) is the clearest large-scale case: a missing bounds check in OpenSSL's heartbeat extension let attackers read up to 64KB of process memory per request, leaking private keys and session tokens from an estimated 17% of the internet's TLS-secured servers at the time. SWEET32 (CVE-2016-2183) showed that legacy 64-bit block ciphers like 3DES were birthday-attackable over long-lived TLS connections, which is why 3DES was formally deprecated. The RC4 stream cipher's biases (tracked through CVE-2013-2566 and later work) removed it from every modern TLS suite. And Juniper's ScreenOS backdoor (CVE-2015-7755) stemmed from a tampered Dual_EC_DRBG random number generator — a textbook insufficient-entropy failure that made VPN traffic decryptable by anyone holding the injected constant.
Vulnerable versus fixed code
Password storage is where cryptographic failures cause the most lasting damage.
# VULNERABLE: fast, unsalted hash — crackable at billions/sec (CWE-916, CWE-759)
import hashlib
def store_password(password):
return hashlib.sha256(password.encode()).hexdigest()
# FIXED: memory-hard, salted, tunable KDF designed for passwords
from argon2 import PasswordHasher
ph = PasswordHasher() # Argon2id defaults per OWASP guidance
def store_password(password):
return ph.hash(password) # salt + parameters embedded in the output
def verify_password(stored_hash, password):
return ph.verify(stored_hash, password)
SHA-256 is a general-purpose hash built for speed, which is exactly wrong for passwords. Argon2id (winner of the 2015 Password Hashing Competition and OWASP's recommended default) is memory-hard and deliberately slow, and it embeds a unique salt automatically, so two identical passwords never produce the same stored value.
Prevention checklist
- Classify data first; encrypt everything sensitive in transit and at rest.
- Enforce TLS 1.2 or higher everywhere, including internal service-to-service traffic, and disable legacy protocols and ciphers.
- Hash passwords with Argon2id, scrypt, or bcrypt — never MD5, SHA-1, or a bare SHA-256.
- Use authenticated encryption such as AES-256-GCM; never ECB mode.
- Generate keys and salts from a cryptographically secure random source.
- Store keys in a dedicated KMS or HSM with defined rotation intervals — never in source code or environment files.
- Scan commit history and dependencies for hardcoded secrets and weak cryptographic primitives.
- Do not retain sensitive data you do not need; unencrypted data that should have been deleted is a recurring root cause.
How Safeguard helps
Safeguard finds cryptographic failures across code, dependencies, and configuration, then uses reachability analysis to determine whether a vulnerable crypto library or a deprecated algorithm call is actually invoked in a path attackers can reach — cutting through findings that never execute in production. Our SCA engine flags dependencies that pull in weak or outdated cryptographic primitives, and Griffin AI correlates hardcoded secrets, weak cipher usage, and TLS misconfigurations against real deployment context to prioritize what carries actual breach risk. Safeguard DAST probes live endpoints for downgraded TLS and missing HSTS, while Auto-Fix opens pull requests that upgrade vulnerable libraries or swap deprecated calls for modern equivalents. For a side-by-side look at how this compares to legacy scanners, see our comparison against Snyk.
Frequently Asked Questions
Why did OWASP rename Sensitive Data Exposure to Cryptographic Failures?
Because "Sensitive Data Exposure" described the outcome, not the cause. Teams read it as an incident label rather than a class of bugs to prevent. Renaming it to Cryptographic Failures points engineers at the actual root causes — weak algorithms, poor key management, missing encryption — that they can find and fix in code and configuration before any exposure happens.
Is HTTPS enough to satisfy A02?
No. TLS protects data in transit, but A02 also covers data at rest, password hashing, key management, and randomness. An application can use flawless HTTPS and still store passwords as unsalted SHA-1 or keep API keys hardcoded in the repository. You need encryption on both sides of the wire plus sound key and secret management.
Are dynamic scanners good at finding cryptographic failures?
Only partly. DAST can detect transport-layer issues like weak TLS, expired certificates, or missing HSTS by probing live endpoints. It generally cannot see how data is stored at rest or which hashing algorithm your code uses, so cryptographic failures require code-level and configuration analysis alongside dynamic testing.
What should I use instead of MD5 or SHA-1 for passwords?
Use a password-specific key derivation function: Argon2id is the recommended default, with scrypt and bcrypt as solid alternatives. These are deliberately slow and, in Argon2 and scrypt's case, memory-hard, which defeats the massive parallelism that makes GPU cracking of fast hashes trivial. General-purpose hashes like SHA-256 are the wrong tool no matter how many rounds you add.
Ready to catch weak cryptography before it ships? Start scanning at app.safeguard.sh/register, or read the setup guide at docs.safeguard.sh.