Use of a non-random IV or nonce in a block cipher mode
ID |
dart.cryptography.non_random_iv_with_cbc_mode |
Severity |
critical |
Remediation Complexity |
medium |
Remediation Risk |
medium |
Remediation Effort |
medium |
Resource |
Cryptography |
Language |
Dart |
Description
A block-cipher mode that requires a unique IV / nonce per encryption (CBC, CTR, GCM, …) is invoked with a static, all-zero, or attacker- predictable initialization vector. Reusing the IV across messages leaks plaintext and, for GCM specifically, leaks the authentication key.
Rationale
CBC, CTR, GCM, CCM, and OFB all derive their security from the uniqueness of their IV / nonce. The specific failure mode depends on the algorithm:
-
CBC with a fixed IV: identical plaintext prefixes produce identical ciphertext prefixes, leaking equality and enabling block-substitution attacks similar to ECB. With a predictable IV, BEAST-style chosen- plaintext attacks become possible.
-
CTR / GCM with a reused nonce: the keystream repeats. XORing two ciphertexts cancels the keystream and yields the XOR of the plaintexts — recoverable by frequency analysis. For GCM, nonce reuse also leaks the authentication subkey, allowing the attacker to forge arbitrary valid ciphertexts (forbidden-attack on AES-GCM).
In Dart this typically appears as IV.fromLength(16) (all zeros),
IV.fromUtf8("static-string"), Uint8List(N) passed to
ParametersWithIV, or a nonce: const [0,0,0,…] argument to
cipher.encrypt(…) in package:cryptography.
The following code illustrates a vulnerable pattern detected by this rule:
class StaticIVs {
void encryptIVs() {
// VULNERABLE: Use of a non-random IV or nonce in a block cipher mode
final iv1 = IV.fromLength(16);
// VULNERABLE: Use of a non-random IV or nonce in a block cipher mode
final iv2 = IV.allZerosOfLength(16);
// VULNERABLE: Use of a non-random IV or nonce in a block cipher mode
final iv3 = IV.fromUtf8("1234567890123456");
// VULNERABLE: Use of a non-random IV or nonce in a block cipher mode
final iv4 = IV.fromBase64("AAAAAAAAAAAAAAAAAAAAAA==");
// VULNERABLE: Use of a non-random IV or nonce in a block cipher mode
final iv5 = IV(Uint8List(16));
// VULNERABLE: Use of a non-random IV or nonce in a block cipher mode
final iv6 = IV(Uint8List.fromList([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]));
print('$iv1 $iv2 $iv3 $iv4 $iv5 $iv6');
}
}
Remediation
Generate a fresh random IV per encryption using a cryptographically
secure RNG, then store it alongside the ciphertext (an IV is not
secret). Higher-level APIs do this for you: IV.fromSecureRandom(16),
cipher.newNonce(), or letting `package:cryptography’s AEAD
constructors generate the nonce automatically.
// Vulnerable
final iv = IV.fromLength(16); // all zeros
final iv2 = IV.fromUtf8('STATIC_IV_FOR_DVFA');
final p = ParametersWithIV(KeyParameter(k), Uint8List(16));
// Fixed: random IV per encryption
final iv = IV.fromSecureRandom(16);
final encrypter = Encrypter(AES(key, mode: AESMode.cbc));
final ciphertext = encrypter.encrypt(plain, iv: iv);
// Persist (iv.bytes, ciphertext) together — iv is not secret.
References
-
OWASP Top 10 2025 - A04 : Cryptographic Failures.
-
CWE-1204 — Generation of Weak Initialization Vector — full taxonomy across modes
-
Joux — Authentication Failures in NIST GCM — "Forbidden Attack" — nonce reuse in AES-GCM reveals the auth key
-
package:encrypt (pub.dev) — Use
IV.fromSecureRandom(blockSize), neverIV.fromLengthfor production