digininja/DVWA · error · Exception

IV must be 16 bytes, {strlen($iv)} passed

Error message

IV must be 16 bytes, {strlen($iv)} passed

What it means

decrypt() in the high-level token library enforces that the IV is exactly 16 bytes, the block size required by aes-128-cbc; encrypt() (line 12) carries the same guard. The token JSON's iv field is base64-decoded before the call, and any decoded length other than 16 raises this Exception with the actual length interpolated into the message.

Source

Thrown at vulnerabilities/cryptography/source/token_library_high.php:24

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) != 16) {
		throw new Exception ("IV must be 16 bytes, " . strlen ($iv) . " passed");
	}
	$tag = "";
	$e = openssl_encrypt($plaintext, ALGO, KEY, OPENSSL_RAW_DATA, $iv, $tag);
	if ($e === false) {
		throw new Exception ("Encryption failed");
	}
	return $e;
}

function decrypt ($ciphertext, $iv) {
	if (strlen ($iv) != 16) {
		throw new Exception ("IV must be 16 bytes, " . strlen ($iv) . " passed");
	}
	$e = openssl_decrypt($ciphertext, ALGO, KEY, OPENSSL_RAW_DATA, $iv);
	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 ($debug = false) {
	$token = "userid:2";

	if ($debug) {
		print "Clear text token: " . $token . "\n";
		print "Encryption key: " . KEY . "\n";
		print "IV: " . (IV) . "\n";

View on GitHub (pinned to 5d5c76cced)

Solutions

  1. Set the iv field to base64 of exactly 16 bytes - the original token's value base64("1234567812345678") = "MTIzNDU2NzgxMjM0NTY3OA==" works.
  2. When forging tokens for this level, leave the issued iv value untouched.
  3. Never copy nonce values produced for aes-256-gcm (12 bytes) into this CBC library.
  4. Pre-validate strlen($iv) === 16 before calling decrypt().

Example fix

// before
$iv = base64_decode($data_array['iv']);
$d = decrypt ($ciphertext, $iv);
// after
$iv = base64_decode($data_array['iv'], true);
if ($iv === false || strlen($iv) !== 16) {
    return json_encode(["status" => 524, "message" => "IV must be 16 bytes"]);
}
$d = decrypt ($ciphertext, $iv);
Defensive patterns

Strategy: validation

Validate before calling

$iv = base64_decode($data_array['iv'], true);
if ($iv === false || strlen($iv) !== 16) {
    // reject before decrypt() can throw
    return json_encode(['status' => 524, 'message' => 'IV must be 16 bytes']);
}

Type guard

function isValidCbcIv(string $iv): bool
{
    return strlen($iv) === 16;
}

Try / catch

try {
    $d = decrypt($ciphertext, $iv);
} catch (Exception $e) {
    if (str_starts_with($e->getMessage(), 'IV must be 16 bytes')) {
        $ret = ['status' => 524, 'message' => 'Missing or malformed IV'];
    } else {
        $ret = ['status' => 526, 'message' => 'Unable to decrypt token'];
    }
}

Prevention

When it happens

Trigger: Supplying a token JSON whose iv base64-decodes to something other than 16 bytes (e.g. "AAAA" = 4 bytes); reusing a 12-byte GCM nonce from token_library_impossible.php; hand-crafting iv strings without proper padding; base64_decode of URL-safe or whitespace-damaged input yielding a wrong-length string (strict mode is not used).

Common situations: Mixing the CBC (16-byte IV) and GCM (12-byte IV) libraries between security levels; migrating code between cipher families without updating IV length; cross-language token generation where another runtime's defaults differ; copy-paste corruption of base64.

Related errors


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