Cryptographic Failures: Using Crypto Correctly

Intermediate
12 min

Cryptographic Failures: Using Crypto Correctly

"Cryptographic Failures" is the second item in the OWASP Top 10, and it is rarely about broken mathematics. It is about choosing the wrong primitive, reusing a nonce, hard-coding a key, or forgetting to encrypt at all. The previous lesson explained how encryption works; this one shows how to apply it correctly.

After this lesson you will be able to classify data that needs protection, encrypt it with an authenticated cipher in Node.js, generate secure random values, and manage keys so one leak does not expose everything.

What Counts as a Cryptographic Failure

OWASP groups several distinct mistakes under this heading:

  • Sensitive data sent or stored in clear text (HTTP instead of HTTPS, plain card numbers in a database).
  • Weak or obsolete algorithms: MD5 or SHA-1 for passwords, DES, RC4, 1024-bit RSA.
  • Missing integrity: encryption without an authentication tag lets an attacker flip bits undetected.
  • Poor randomness: Math.random() for tokens, predictable IVs, reused nonces.
  • Bad key management: keys in Git, one key for every environment, no rotation.

Step 1: Classify Before You Encrypt

Encryption has a cost, so decide what actually needs it. A simple three-tier classification is enough for most applications:

| Tier | Examples | Requirement | |---|---|---| | Public | Product catalogue, blog posts | TLS in transit only | | Internal | User IDs, order history | TLS in transit, access control at rest | | Sensitive | Passwords, card data, government IDs, health data | TLS in transit, encrypted or hashed at rest, audit logged |

Passwords are a category of their own: hashed with Argon2id or bcrypt, never encrypted, because you must never be able to recover them.

Step 2: Use an Authenticated Cipher

For data you must read back later (a third-party API key, a stored document), use AES-256-GCM. GCM produces an authentication tag, so tampering is detected on decryption. The sample code above shows the full pattern; the important details are:

  • A fresh 12-byte random IV per message. Reusing an IV with the same key in GCM breaks security completely.
  • IV and tag are stored alongside the ciphertext; they are not secret.
  • decipher.final() throws if the tag does not match, so tampering fails loudly.

Never use aes-256-ecb (identical blocks give identical output) or CBC without a separate MAC.

Step 3: Generate Randomness Correctly

Session IDs, password reset tokens and CSRF tokens must be unpredictable. Math.random() is seeded from the clock and can be reconstructed by an observer.

javascript
const crypto = require("node:crypto"); const resetToken = crypto.randomBytes(32).toString("hex"); // 256 bits of entropy const id = crypto.randomUUID(); // v4 UUID from a CSPRNG const pin = crypto.randomInt(0, 1_000_000).toString().padStart(6, "0"); console.log(resetToken.length, id, pin); // 64 chars, a UUID, a 6-digit PIN

Store the hash of a reset token and send the original to the user; a leaked database then yields nothing usable.

Step 4: Compare Secrets in Constant Time

A normal === comparison returns as soon as the first byte differs. Over many requests that timing difference reveals a secret byte by byte.

javascript
function safeEqual(a, b) { const bufA = Buffer.from(a), bufB = Buffer.from(b); if (bufA.length !== bufB.length) return false; return crypto.timingSafeEqual(bufA, bufB); }

Use this whenever you verify webhook signatures, API keys or one-time codes.

Step 5: Manage Keys Like Production Secrets

The strongest cipher is useless if the key sits in the repository. Load keys from an environment variable injected by a secrets manager or a cloud KMS, use different keys per environment, and plan rotation from day one by storing a key ID with each ciphertext:

json
{ "kid": "2026-03", "iv": "…", "tag": "…", "data": "…" }

On rotation, new records use the new key while old ones stay readable until a job re-encrypts them.

Quick Quiz
Question 1 of 2

Why must the IV be unique for every message when using AES-GCM?

Key Takeaways

  • Cryptographic failures are usually misuse: wrong algorithm, reused nonce, hard-coded key, or no encryption at all.
  • Classify data first; hash passwords, encrypt sensitive data at rest, and use TLS everywhere.
  • AES-256-GCM with a fresh random IV gives confidentiality and integrity together.
  • Use crypto.randomBytes, randomUUID and randomInt for anything secret; never Math.random().
  • Compare secrets with timingSafeEqual, keep keys out of the repo, and store a key ID for rotation.

Next lesson: Network Security Basics: Firewalls, VPNs and Ports — how traffic is controlled at the network layer before it reaches your code.

Cryptographic Failures: Using Crypto Correctly - Cyber Security | CodeYourCraft | CodeYourCraft