Use of a weak cryptographic initialization vector

ID

javascript.weak_encryption_initialization_vector

Severity

critical

Remediation Complexity

trivial

Remediation Risk

medium

Remediation Effort

low

Resource

Cryptography

Language

JavaScript

Tags

ASVS50:v11.3.1, CCN-PITFALL/SymEncryption.1, CWE:1204, NIST.SP.800-53, OWASP:2025:A04, PCI-DSS:6.5.3, crypto

Description

The improper use of initialization vectors (IVs) in encryption can weaken data security by enabling pattern detection and making the ciphertext vulnerable to attacks. It’s critical to ensure the IV is generated securely and used correctly to maintain encryption strength.

Rationale

Initialization vectors (IVs) are crucial in encryption schemes like CBC or GCM to ensure the same plaintext results in different ciphertexts. A weak or improperly used IV, such as a static or predictable one, can allow attackers to uncover patterns in the data, leading to potential data leaks or unauthorized access.

The rule reports a fixed IV given to a cipher, such as Buffer.alloc(16, 0), Buffer.from('0123456789abcdef') or a constant holding one.

Consider the following JavaScript code, which encrypts every message with the same all-zero IV:

const crypto = require('crypto');

const KEY = crypto.randomBytes(32);
const STATIC_IV = Buffer.alloc(16, 0);

function encrypt(plain) {
  const cipher = crypto.createCipheriv('aes-256-cbc', KEY, STATIC_IV); // FLAW
  return Buffer.concat([cipher.update(plain, 'utf8'), cipher.final()]).toString('hex');
}

module.exports = { encrypt };

Two messages that start with the same block produce ciphertexts that start with the same block, so an observer learns which messages share a prefix. With a stream-like mode such as GCM or CTR, reusing the IV with the same key is far worse: it discloses the XOR of the plaintexts and, for GCM, lets an attacker forge messages.

Remediation

To remediate issues with weak or improperly used IVs, ensure they are generated securely and uniquely for each encryption operation.

Generate a fresh IV with crypto.randomBytes (or crypto.getRandomValues in the browser) for every encryption, and store it next to the ciphertext: the IV does not need to be secret.

const crypto = require('crypto');

const KEY = crypto.randomBytes(32);

function encrypt(plain) {
  const iv = crypto.randomBytes(16);
  const cipher = crypto.createCipheriv('aes-256-cbc', KEY, iv);
  const data = Buffer.concat([cipher.update(plain, 'utf8'), cipher.final()]);
  return iv.toString('hex') + ':' + data.toString('hex');
}

module.exports = { encrypt };

References

© 2026 Xygeni. All rights reserved