PHP Type Juggling is a vulnerability arising from PHP's loose type comparison system, where values of different types are automatically converted for comparison, potentially leading to authentication bypasses and other security issues.
# Loose comparison (==) does type coercion
"0" == 0 // true (string converted to int)
"abc" == 0 // true (non-numeric string = 0)
"0e123" == 0 // true ("scientific notation" = 0)
"1abc" == 1 // true (leading number extracted)
true == "any" // true (non-empty string = true)
null == "" // true
// Vulnerable code
$password = $_POST['password'];
$correct = "secret123";
if ($password == $correct) {
authenticate();
}
// Attack: If password starts with 0e and contains only digits
// "0e123456" == "0e789012" → 0 == 0 → true!
# Hashes that look like scientific notation
md5('240610708') = 0e462097431906509019562988736854
md5('QNKCDZO') = 0e830400451993494058024219903391
sha1('10932435112')= 0e07766915004133176347055865026311692244
# When compared with ==, these equal 0 (and each other)
# PHP < 8.0 ONLY!
Important: PHP 8.0 changed string-to-number comparison behavior. The "0e" magic hash trick no longer works in PHP 8+ because non-numeric strings are no longer coerced to 0. However, type juggling issues still exist with:
"0" == "0.0" is still truetrue == "anystring" is still true{"password": true}// API expects string password
{"password": "admin"}
// Attacker sends integer or boolean
{"password": 0} // May bypass checks
{"password": true} // May match any string
===) not loose (==)hash_equals() for hash comparisons