CWE-321 (use of hard-coded cryptographic key) describes the failure where the key and the data it protects are co-located — making the key trivially recoverable from anyone who can read the codebase, a git history export, or an environment variable committed to source control. NIST SC-12 requires that cryptographic keys be generated, distributed, stored, and destroyed through a formal key management process. PCI-DSS 4.0 Req-3.7 requires split knowledge and dual control for key custodianship. An encryption key stored next to the data it encrypts — whether in a .env committed to git, config/keys.json, or hardcoded in source — means any code read gives an attacker both the ciphertext and the key to decrypt it. OWASP 2021 A02 explicitly cites key co-location as a cryptographic failure root cause.
High because a key stored alongside encrypted data completely defeats encryption — any attacker with codebase read access can decrypt all protected records without brute force.
Migrate encryption key loading to a dedicated KMS. AWS KMS, GCP Cloud KMS, and Azure Key Vault all provide key material that never leaves the service — your application receives ciphertext, not the raw key. For AWS:
// src/lib/kms.ts
import { KMSClient, EncryptCommand, DecryptCommand } from '@aws-sdk/client-kms';
const client = new KMSClient({ region: process.env.AWS_REGION });
export async function kmsEncrypt(plaintext: string): Promise<string> {
const cmd = new EncryptCommand({
KeyId: process.env.AWS_KMS_KEY_ID!,
Plaintext: Buffer.from(plaintext),
});
const result = await client.send(cmd);
return Buffer.from(result.CiphertextBlob!).toString('base64');
}
export async function kmsDecrypt(ciphertext: string): Promise<string> {
const cmd = new DecryptCommand({
CiphertextBlob: Buffer.from(ciphertext, 'base64'),
});
const result = await client.send(cmd);
return Buffer.from(result.Plaintext!).toString('utf8');
}
Remove all key material from the codebase. If a key was ever committed to git, rotate it immediately — git history is permanent.
finserv-encryption.key-management.keys-in-kmshigh"0 KMS integrations found — encryption key loaded from config/keys.json in codebase" or "1 of 3 key-loading paths reads from .env file committed to git"finserv-encryption.key-management.key-rotation-annual for rotation policy, and finserv-encryption.pci-alignment.environment-specific-keys for per-environment key separation.// AWS KMS example
import AWS from 'aws-sdk';
const kms = new AWS.KMS();
const encryptData = async (plaintext: string) => {
const result = await kms.encrypt({
KeyId: process.env.AWS_KMS_KEY_ID!,
Plaintext: plaintext,
}).promise();
return result.CiphertextBlob.toString('base64');
};
// Example: Using Supabase Vault
const { data, error } = await supabase
.from('secrets')
.update({ value: supabase.vault.encrypt('key_data') })
.eq('name', 'encryption_key');
git rm .env # Remove committed env file
echo ".env" >> .gitignore