digininja/DVWA · error · Exception
IV must be 12 bytes, {strlen($iv)} passed
Error message
IV must be 12 bytes, {strlen($iv)} passed What it means
decrypt() in the impossible-level token library enforces a 12-byte IV, the recommended nonce size for aes-256-gcm; encrypt() (line 11) applies the same guard. The token JSON carries the nonce base64-encoded in its iv field, and any decoded length other than 12 throws with the actual length in the message.
Source
Thrown at vulnerabilities/cryptography/source/token_library_impossible.php:23
function encrypt ($plaintext, $iv) {
# Default padding is PKCS#7 which is interchangeable with PKCS#5
# https://en.wikipedia.org/wiki/Padding_%28cryptography%29#PKCS#5_and_PKCS#7
if (strlen ($iv) != 12) {
throw new Exception ("IV must be 12 bytes, " . strlen ($iv) . " passed");
}
$e = openssl_encrypt($plaintext, ALGO, KEY, OPENSSL_RAW_DATA, $iv, $tag);
if ($e === false) {
throw new Exception ("Encryption failed");
}
return $e . $tag;
}
function decrypt ($ciphertext, $iv) {
if (strlen ($iv) != 12) {
throw new Exception ("IV must be 12 bytes, " . strlen ($iv) . " passed");
}
$tag = substr($ciphertext, -16);
$text = substr($ciphertext, 0, -16);
$e = openssl_decrypt($text, ALGO, KEY, OPENSSL_RAW_DATA, $iv, $tag);
if ($e === false) {
throw new Exception ("Decryption failed");
}
return $e;
}
// Added the debug flag so that when calling from the script
// the function can print the data used to create the token
function create_token () {
$token = "userid:2";
$iv = openssl_random_pseudo_bytes(12, $cstrong);View on GitHub (pinned to 5d5c76cced)
Solutions
- Use base64 of exactly 12 random bytes - openssl_random_pseudo_bytes(12) as create_token() does.
- Leave the issued token's iv field untouched when only modifying other parts.
- Never mix this library's tokens with the CBC library's 16-byte IVs.
- Pre-validate strlen($iv) === 12 (and strict base64_decode) before calling decrypt().
Example fix
// before $iv = openssl_random_pseudo_bytes(16); // after $iv = openssl_random_pseudo_bytes(12);
Defensive patterns
Strategy: validation
Validate before calling
$iv = base64_decode($data_array['iv'], true);
if ($iv === false || strlen($iv) !== 12) {
// reject before decrypt() can throw
return json_encode(['status' => 524, 'message' => 'IV must be 12 bytes']);
} Type guard
function isValidGcmNonce(string $iv): bool
{
return strlen($iv) === 12;
} Try / catch
try {
$d = decrypt($ciphertext, $iv);
} catch (Exception $e) {
if (str_starts_with($e->getMessage(), 'IV must be 12 bytes')) {
$ret = ['status' => 524, 'message' => 'Missing or malformed IV'];
} else {
$ret = ['status' => 526, 'message' => 'Unable to decrypt token'];
}
} Prevention
- Derive the nonce length from the cipher (12 for GCM, 16 for CBC) instead of hard-coding per call site.
- Always base64_decode(..., true) so malformed input fails deterministically.
- Never reuse a nonce across tokens with the same key - GCM nonce reuse is catastrophic.
When it happens
Trigger: Copying the 16-byte CBC IV ("MTIzNDU2NzgxMjM0NTY3OA==") from the high-level library into a GCM token; hand-built iv strings of the wrong size; base64_decode of URL-safe (-, _) or whitespace-damaged input; generating nonces with random_bytes(16) instead of 12.
Common situations: Migrating CBC code to GCM without changing the IV length; mixing token libraries between DVWA security levels; interop with stacks whose examples default to 16-byte nonces; non-strict base64 decoding of user input.
Related errors
- IV must be 16 bytes, {strlen($iv)} passed
- Decryption failed
- Decryption failed
- Decryption failed
- Could not decode JSON object.
AI-assisted analysis of digininja/DVWA@5d5c76cced (2026-08-21).
Data as JSON: /api/errors/9cbea1230750e9ee.
Report an issue: GitHub.