Cryptographic Failures: Protecting Sensitive Data Properly

A
Author
03 May, 2026 2 min read 883 views

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.

Leave a comment

Comments are reviewed before they appear.