Description
Older ciphers such as DES/3DES, RC2/RC4, and Blowfish, or inappropriate modes such as AES-ECB, can weaken confidentiality or integrity depending on their use and configuration. DES's short key, RC4's known weaknesses, and ECB's exposure of repeated block patterns are different problems. MD5 and SHA-1 are vulnerable to collisions and unsuitable where integrity checks or signatures require collision resistance.
Potential impact
- Exposure of plaintext patterns or sensitive information through weak encryption
- Forged integrity checks or signatures when vulnerable hashes permit collisions
- Authentication or authorization bypass where weak cryptographic designs enable token or message forgery
Remediation
- Prefer authenticated encryption such as AES-GCM or ChaCha20-Poly1305, and hashes such as SHA-256/384/512 or SHA-3.
- Avoid DES/3DES, RC2/RC4, Blowfish, MD5, SHA-1, and AES-ECB for these security uses.
- Use the recommended 96-bit IV for AES-GCM. Never reuse an IV with the same key, and manage per-key usage and collision risk. Use at least 128-bit keys, preferably 256-bit.
- Derive password-based keys with PBKDF2, Scrypt, or Argon2 and appropriate iteration or memory costs.
- Prefer AEAD. Where it cannot be used, provide separate authentication such as HMAC-SHA-256.
- Use established libraries such as the Web Crypto API (
node:cryptowebcrypto) or libsodium.
Examples
Before
javascript
const crypto = require("crypto");
// Before: weak encryption mode and hash.
function encryptEcb(plaintext, key) {
// AES-ECB exposes patterns and provides no integrity protection.
const cipher = crypto.createCipheriv("aes-128-ecb", key, null);
return Buffer.concat([cipher.update(plaintext, "utf8"), cipher.final()]);
}
function md5Checksum(data) {
// MD5 lacks the collision resistance needed for security integrity checks.
return crypto.createHash("md5").update(data).digest("hex");
}
// Fixed demonstration key (also insecure).
const key = Buffer.alloc(16, 0);
console.log(encryptEcb("secret text", key).toString("hex"));
console.log(md5Checksum("file-contents"));
After
javascript
const crypto = require("crypto");
// AES-256-GCM for confidentiality and integrity.
function encryptAesGcm(plaintext, key) {
const iv = crypto.randomBytes(12); // Recommended 96-bit IV.
const cipher = crypto.createCipheriv("aes-256-gcm", key, iv);
const ciphertext = Buffer.concat([
cipher.update(plaintext, "utf8"),
cipher.final(),
]);
const tag = cipher.getAuthTag();
return {
iv: iv.toString("hex"),
ct: ciphertext.toString("hex"),
tag: tag.toString("hex"),
};
}
function decryptAesGcm(enc, key) {
const iv = Buffer.from(enc.iv, "hex");
const ct = Buffer.from(enc.ct, "hex");
const tag = Buffer.from(enc.tag, "hex");
const decipher = crypto.createDecipheriv("aes-256-gcm", key, iv);
decipher.setAuthTag(tag);
const plaintext = Buffer.concat([decipher.update(ct), decipher.final()]);
return plaintext.toString("utf8");
}
// Use a strong hash for integrity checks.
function sha256Checksum(data) {
return crypto.createHash("sha256").update(data).digest("hex");
}
// Use HMAC-SHA-256 for message authentication where needed.
function hmacSha256(data, key) {
return crypto.createHmac("sha256", key).update(data).digest("hex");
}
// Generate a strong key (32 bytes for AES-256).
const key = crypto.randomBytes(32);
const enc = encryptAesGcm("secret text", key);
console.log(enc);
console.log("sha256:", sha256Checksum("file-contents"));
console.log("hmac:", hmacSha256("message", crypto.randomBytes(32)));
Explanation:
- Before: AES-ECB exposes equal plaintext blocks and does not authenticate ciphertext. MD5 collisions undermine collision-resistant integrity checks. The fixed demonstration key also exposes the encrypted data.
- After: AES-256-GCM provides authenticated encryption and checks the tag during decryption. SHA-256 can check integrity against a trusted reference digest, but an unkeyed hash does not authenticate the sender. Generate strong keys and a fresh IV for each encryption, while limiting per-key use and preventing reuse; random IVs still have collision risk.