Cryptographic Failures: Protecting Sensitive Data Properly
Cryptographic failures are rarely about breaking strong maths. They're about not encrypting data at all, using the wrong tool, or handling keys carelessly.
1. Passwords: hash, never encrypt
import argon2 from 'argon2';
const hash = await argon2.hash(password, { type: argon2.argon2id });
const ok = await argon2.verify(hash, attempt);
- Use a slow, salted password hashing function: Argon2id, scrypt or bcrypt.
- Never MD5, SHA-1 or plain SHA-256 for passwords; they're far too fast to brute-force.
- Never store passwords in a reversible form.
2. Encrypt data in transit
- HTTPS everywhere, with HSTS.
- TLS for internal connections too: databases, caches, service-to-service calls.
- TLS 1.2 minimum; prefer 1.3.
3. Encrypt sensitive fields at rest
Disk encryption protects against stolen disks, not against someone with database access. For highly sensitive fields (government IDs, bank details, health data), encrypt in the application with authenticated encryption:
import { createCipheriv, createDecipheriv, randomBytes } from 'node:crypto';
const KEY = Buffer.from(process.env.FIELD_KEY, 'base64'); // 32 bytes, from a secret manager
export function encrypt(plaintext) {
const iv = randomBytes(12); // unique per encryption
const cipher = createCipheriv('aes-256-gcm', KEY, iv);
const data = Buffer.concat([cipher.update(plaintext, 'utf8'), cipher.final()]);
return [iv, cipher.getAuthTag(), data].map((b) => b.toString('base64')).join('.');
}
export function decrypt(payload) {
const [iv, tag, data] = payload.split('.').map((s) => Buffer.from(s, 'base64'));
const decipher = createDecipheriv('aes-256-gcm', KEY, iv);
decipher.setAuthTag(tag); // detects tampering
return Buffer.concat([decipher.update(data), decipher.final()]).toString('utf8');
}
4. Manage keys properly
- Keys live in a secret manager or KMS, never in code or the repository.
- Plan for rotation: store a key ID with each ciphertext.
- Use envelope encryption with a cloud KMS for large volumes of data.
5. Use secure randomness
Tokens, IDs and reset codes must come from a cryptographically secure source: crypto.randomBytes, crypto.randomUUID or Python's secrets module, never Math.random().
6. Don't store what you don't need
The safest sensitive data is data you never kept. Don't store full card numbers (use a payment provider's tokens), and delete data when it's no longer needed.
Was this article helpful?
Comments
No comments yet. Have a question or a tip? Be the first to share it.