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.