← All fixes

Fix it

Hardcoded encryption key or IV — why it breaks your crypto and how to fix it

High severityCWE-321 (Use of Hard-coded Cryptographic Key)
We scanned nearly 2,000 AI-built apps and 1 in 8 shipped a high-severity flaw. Is this one in yours?Scan free →

Encryption only protects data if the key stays secret and the initialization vector is unique per message. A key written into source code is not secret, so anyone with the repo, the bundle, or a decompiled build can decrypt everything it protected. A fixed IV reused across messages leaks structure and can undermine the cipher entirely. Load the key from a secret store and generate a fresh random IV for each encryption.

Why it's a problem

A hardcoded key means a single leak of your code is a leak of all the data you encrypted with it, and rotating it means re-encrypting everything. A static IV with a block cipher mode like CBC or CTR causes identical plaintexts to produce identical or predictable ciphertexts, which can reveal content and, in some modes, allow recovery of the plaintext. The data looks encrypted while offering far less protection than intended.

The pattern

import crypto from "node:crypto";

const KEY = "0123456789abcdef0123456789abcdef"; // hardcoded
const IV = Buffer.alloc(16, 0);                  // static IV

function encrypt(text) {
  const c = crypto.createCipheriv("aes-256-cbc", KEY, IV);
  return Buffer.concat([c.update(text, "utf8"), c.final()]).toString("hex");
}

The fix

import crypto from "node:crypto";

const KEY = Buffer.from(process.env.ENCRYPTION_KEY, "hex"); // 32 bytes from secrets

function encrypt(text) {
  const iv = crypto.randomBytes(12);                        // fresh per message
  const c = crypto.createCipheriv("aes-256-gcm", KEY, iv);
  const enc = Buffer.concat([c.update(text, "utf8"), c.final()]);
  const tag = c.getAuthTag();
  // store iv + tag alongside the ciphertext so you can decrypt later
  return Buffer.concat([iv, tag, enc]).toString("hex");
}

Why AI tools write this

To produce a runnable encryption example, the assistant needs concrete values, so it invents a key and a zero IV inline because that makes the snippet execute. It has no access to your secret store and no signal that the example will be pasted into production unchanged, so the placeholder key and fixed IV ship as real configuration.

The quick fix

  • Load the key from an environment variable or secret manager, never from source.
  • Generate a fresh random IV or nonce for every encryption and store it with the ciphertext.
  • Prefer an authenticated mode like AES-256-GCM over unauthenticated CBC.
  • Rotate any key that was ever committed, and re-encrypt data protected by it.

Want to know if this pattern is already in a repo you shipped? Scan a public repo free, no account needed.

Scan a repo →

Related: Hardcoded JWT secret

Hardcoded Encryption Key / Static IV — Why It's Broken and the Fix: Prbl