PHP Type Juggling

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.

How Type Juggling Works

# 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

Authentication Bypass Example

// 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!

Magic Hashes

# 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!

PHP 8.0+ Changes

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:

  • Comparisons between numeric strings: "0" == "0.0" is still true
  • Boolean comparisons: true == "anystring" is still true
  • JSON input where attacker controls type: {"password": true}

JSON Type Confusion

// API expects string password
{"password": "admin"}

// Attacker sends integer or boolean
{"password": 0}     // May bypass checks
{"password": true}  // May match any string

Prevention

  • Use strict comparison (===) not loose (==)
  • Use hash_equals() for hash comparisons
  • Validate input types explicitly
  • Use JSON schema validation

See Also