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

  1. Use base64 of exactly 12 random bytes - openssl_random_pseudo_bytes(12) as create_token() does.
  2. Leave the issued token's iv field untouched when only modifying other parts.
  3. Never mix this library's tokens with the CBC library's 16-byte IVs.
  4. 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

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


AI-assisted analysis of digininja/DVWA@5d5c76cced (2026-08-21). Data as JSON: /api/errors/9cbea1230750e9ee. Report an issue: GitHub.