← 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.

Common questions

If I rotate a hardcoded encryption key, what happens to data already encrypted with the old key?

It becomes unreadable with the new key. You must re-encrypt all existing data with the new key during rotation. Plan the data migration before rotating in production.

Does a static IV make the encryption completely broken?

It depends on the cipher mode. In CBC, a static IV with identical plaintexts produces identical ciphertexts, leaking patterns. In CTR or GCM, reusing an IV with the same key is more seriously broken and can enable plaintext recovery.

Is AES-256-GCM preferred over AES-256-CBC?

Yes. GCM is an authenticated encryption mode that detects tampering without a separate MAC step. CBC is malleable without a separate MAC and requires additional integrity protection to be safe.

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

Scan my app →

Related: Hardcoded JWT secret

Catch this automatically: scan your GitHub repo · website vulnerability scanner · review every pull request · SAST for AI code · OWASP Top 10 for AI code

Hardcoded Encryption Key / Static IV, Why It's Broken and the Fix | Prbl