Cryptographic Failures
Protect sensitive data with proper encryption, hashing, and TLS — avoid exposing secrets through weak crypto.
URL: trusteed.io/academy/web-security/cryptographic-failures
Cryptographic Failures cover any weakness in how an application protects data through encryption — data transmitted or stored in plaintext that should be encrypted, weak or outdated cryptographic algorithms, hardcoded or poorly managed encryption keys, and improper certificate validation. This category was renamed from "Sensitive Data Exposure" in the 2021 OWASP revision specifically to emphasize that the failure is usually in the cryptography itself, not just in the fact that data was exposed.
What Are Cryptographic Failures?
The category covers the full lifecycle of protecting sensitive data: identifying what data actually needs protection (PII, credentials, financial data, health data, session tokens), encrypting it both at rest and in transit using current, strong algorithms, and managing the keys that make that encryption meaningful. Common failures include transmitting sensitive data over unencrypted HTTP, using deprecated algorithms (MD5, SHA1, DES) for anything security-relevant, hardcoding encryption keys or secrets directly in source code, disabling TLS certificate validation to work around an integration problem, and storing passwords with fast, general-purpose hashes instead of purpose-built slow hashing algorithms.
Why AI Coding Assistants Get This Wrong
Working code that skips encryption or uses whatever hashing function is fastest to type (md5(password) is one line; bcrypt.hash(password, 12) with proper async handling is a few more) looks identical to secure code in every functional test — the application still logs users in, still stores and retrieves data correctly. Cryptographic weaknesses are invisible until specifically probed, which is exactly why they're easy for both humans and AI assistants to defer indefinitely as "we'll harden this later." Hardcoded secrets are similarly common in generated code, since the assistant needs some value to complete a working example and has no way to know your actual secrets management setup unless told explicitly.
Vulnerable Pattern
const crypto = require('crypto');
// Fast, general-purpose hash — crackable at billions of attempts per second on modern GPUs
function hashPassword(password) {
return crypto.createHash('md5').update(password).digest('hex');
}
// Hardcoded secret directly in source
const ENCRYPTION_KEY = 'my-secret-key-123'; // committed to version control
// TLS verification disabled to work around a certificate issue
const client = axios.create({
httpsAgent: new https.Agent({ rejectUnauthorized: false }),
});
Secure Pattern
const bcrypt = require('bcrypt');
const crypto = require('crypto');
// Purpose-built, slow, salted hashing for passwords
async function hashPassword(password) {
return bcrypt.hash(password, 12); // cost factor tuned to current hardware
}
// Secrets loaded from environment/secret manager, never committed to source
const ENCRYPTION_KEY = process.env.ENCRYPTION_KEY; // sourced from a vault/secret manager
if (!ENCRYPTION_KEY) throw new Error('ENCRYPTION_KEY must be set via environment/secret manager');
// AES-GCM for data requiring reversible encryption (not passwords)
function encrypt(plaintext) {
const iv = crypto.randomBytes(12);
const cipher = crypto.createCipheriv('aes-256-gcm', Buffer.from(ENCRYPTION_KEY, 'hex'), iv);
const encrypted = Buffer.concat([cipher.update(plaintext, 'utf8'), cipher.final()]);
return { iv: iv.toString('hex'), data: encrypted.toString('hex'), tag: cipher.getAuthTag().toString('hex') };
}
// TLS verification always enabled
const client = axios.create({ timeout: 10000 }); // default rejectUnauthorized: true, never overridden
Copy-Paste Rules for Cursor, Claude Code, and Codex
---
description: Cryptographic failures prevention — encryption, hashing, key management
alwaysApply: true
---
# Cryptographic Security Rules
- Never hash passwords with md5, sha1, or a single unsalted sha256 pass.
Always use bcrypt, scrypt, or argon2 with an appropriate cost factor.
- Never hardcode encryption keys, API secrets, or credentials directly in
source code. Always load them from environment variables or a secret
manager, and fail loudly at startup if a required secret is missing.
- Never disable TLS certificate verification (`rejectUnauthorized: false` or
equivalent) to work around an integration issue. Fix the underlying
certificate or configuration problem instead.
- All sensitive data in transit MUST use TLS 1.2 or higher. Never transmit
passwords, tokens, or PII over unencrypted HTTP, including on internal
networks.
- Sensitive data at rest (PII, financial data, health data, tokens) MUST be
encrypted using current, strong algorithms (AES-256-GCM or equivalent) —
never a deprecated cipher (DES, RC4) or ECB mode.
- Do not invent custom cryptographic schemes. Use well-established,
audited libraries and standard algorithms rather than a homegrown approach.
- When generating code that stores or transmits data, explicitly classify
whether the data is sensitive and apply the appropriate encryption/hashing
in the same pass — do not treat it as a follow-up hardening step.
Verification Checklist
- Passwords are hashed with bcrypt/argon2/scrypt, never a fast general-purpose hash
- No encryption keys, API secrets, or credentials exist in source code or version control
- TLS certificate verification is enabled on every outbound and inbound connection, with no exceptions
- All sensitive data in transit uses TLS 1.2+; no sensitive data is transmitted over plain HTTP
- Sensitive data at rest is encrypted with a current, strong algorithm
- Secret rotation is possible without a code change (secrets are externalized, not embedded)
- Continuous scanning checks for exposed secrets and weak TLS configuration — see Trusteed's cloud security platform for encryption and configuration monitoring
Frequently asked questions
Is HTTPS alone sufficient to satisfy this category?
No. HTTPS/TLS addresses data in transit, but Cryptographic Failures also cover data at rest, password storage, key management, and certificate validation — all of which need to be addressed independently.
What's wrong with using SHA-256 for passwords if it's a "secure" hash?
SHA-256 is cryptographically secure as a general-purpose hash function, but it's fast — designed for speed, which is exactly the wrong property for password hashing, since it lets an attacker with a leaked hash database attempt billions of guesses per second on modern hardware. Purpose-built password hashing functions (bcrypt, scrypt, argon2) are deliberately slow to make brute-forcing impractical.
How does secret management relate to this category?
Hardcoded secrets are a direct Cryptographic Failure — an encryption key committed to source control provides no real protection, since anyone with repository access (including in a leaked or forked copy) has the key needed to decrypt everything it protects.