{"record":{"id":"b4835ca759449762","repo":"digininja/DVWA","slug":"could-not-decode-json-object","errorCode":null,"errorMessage":"Could not decode JSON object.","messagePattern":"Could not decode JSON object\\.","errorType":"exception","errorClass":"Exception","httpStatus":null,"severity":"warning","filePath":"vulnerabilities/cryptography/source/medium.php","lineNumber":29,"sourceCode":"\n$errors = \"\";\n$success = \"\";\n$messages = \"\";\n\nif ($_SERVER['REQUEST_METHOD'] == \"POST\") {\n\ttry {\n\t\tif (!array_key_exists ('token', $_POST)) {\n\t\t\tthrow new Exception (\"No token passed\");\n\t\t} else {\n\t\t\t$token = $_POST['token'];\n\t\t\tif (strlen($token) % 32 != 0) {\n\t\t\t\tthrow new Exception (\"Token is in wrong format\");\n\t\t\t} else {\n\t\t\t\t$decrypted = decrypt(hex2bin ($token), $key);\n\n\t\t\t\t$user = json_decode ($decrypted);\n\t\t\t\tif ($user === null) {\n\t\t\t\t\tthrow new Exception (\"Could not decode JSON object.\");\n\t\t\t\t}\n\n\t\t\t\tif ($user->user == \"sweep\" && $user->ex > time() && $user->level == \"admin\") {\n\t\t\t\t\t$success = \"Welcome administrator Sweep\";\n\t\t\t\t} else {\n\t\t\t\t\t$messages = \"Login successful but not as the right user.\";\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t} catch(Exception $e) {\n\t\t$errors = $e->getMessage();\n\t}\n}\n\n$html = \"\n\t\t<p>\n\t\tYou have managed to get hold of three session tokens for an application you think is using poor cryptography to protect its secrets:\n\t\t</p>","sourceCodeStart":11,"sourceCodeEnd":47,"githubUrl":"https://github.com/digininja/DVWA/blob/5d5c76cced604e54462b13723f5c69af58e78748/vulnerabilities/cryptography/source/medium.php#L11-L47","documentation":"After the token decrypts successfully, json_decode($decrypted) returned null, meaning the plaintext was not valid JSON. The expected plaintext is an object like {\"user\":\"example\",\"ex\":1723620372,\"level\":\"user\",\"bio\":\"blah\"}; garbage from a bad splice, empty plaintext, or the literal string 'null' all produce a null decode and land here.","triggerScenarios":"Cut-and-pasting ECB blocks so the reassembled plaintext breaks JSON syntax (unbalanced quotes/braces); a decryption that yields binary garbage whose padding happened to validate; plaintext that is empty or the literal 'null' (json_decode('null') === null, a true false-positive of this check); plaintext produced by encrypting data with a different key that still decrypts to something non-JSON.","commonSituations":"ECB block-splicing attacks misaligning field boundaries; wrong-key ciphertext that slips past padding checks; token producers serializing with a different format (query string, CSV) than the consumer expects.","solutions":["Temporarily var_dump($decrypted) to see the actual plaintext reaching json_decode.","When splicing ECB blocks, keep whole 16-byte blocks aligned with the field layout of the valid 'Soo' token (its bio field is the attacker-friendly slot).","Re-encrypt your intended JSON object with the same key/mode and hex-encode it instead of hand-editing.","Use json_decode with JSON_THROW_ON_ERROR to get the precise syntax error position."],"exampleFix":"// before\n$user = json_decode ($decrypted);\nif ($user === null) {\n    throw new Exception (\"Could not decode JSON object.\");\n}\n// after\ntry {\n    $user = json_decode($decrypted, false, 512, JSON_THROW_ON_ERROR);\n} catch (JsonException $e) {\n    throw new Exception(\"Could not decode JSON object: \" . $e->getMessage());\n}","handlingStrategy":"try-catch","validationCode":"// Cheap shape check before json_decode: the documented token format is a JSON object\nif (strlen($decrypted) === 0 || $decrypted[0] !== '{') {\n    throw new Exception('Could not decode JSON object: plaintext is not a JSON object');\n}","typeGuard":"function isDecryptedTokenObject(string $plaintext): bool\n{\n    $decoded = json_decode($plaintext);\n    return $decoded instanceof stdClass\n        && isset($decoded->user, $decoded->ex, $decoded->level);\n}","tryCatchPattern":"use JsonException;\ntry {\n    $user = json_decode($decrypted, false, 512, JSON_THROW_ON_ERROR);\n} catch (JsonException $e) {\n    // JSON_THROW_ON_ERROR distinguishes 'null' input, syntax errors, and depth errors\n    throw new Exception('Could not decode JSON object: ' . $e->getMessage());\n}","preventionTips":["Use JSON_THROW_ON_ERROR instead of comparing against null - json_decode('null') is also null.","Inspect the decrypted plaintext (var_dump/log) whenever decode fails; it almost always reveals splice misalignment.","Validate the decoded object's required fields (user, ex, level) before using them."],"tags":["php","json","json-decode","cryptography","ecb","dvwa"],"backgroundTag":"json-parse-error","analyzedSha":"5d5c76cced604e54462b13723f5c69af58e78748","analyzedAt":"2026-08-21T01:20:26.904Z","schemaVersion":2},"datasetVersion":"2026-08-21T03:17:12.404Z"}