← Home ← Codex ← DEBT
Browse by Category
+ added · updated 7d
← Back to glossary

Type Juggling

Security CWE-704 OWASP A3:2021 CVSS 8.1 PHP 5.0+ Intermediate
debt(d5/e3/b5/t9)
d5 Detectability Operational debt — how invisible misuse is to your safety net

Closest to 'specialist tool catches it' (d5). The detection_hints list phpstan, psalm, and semgrep — all specialist static analysis tools — as able to catch == comparisons with mixed types and missing strict third argument in in_array(). Default linters don't flag this; it requires intentional static analysis configuration.

e3 Effort Remediation debt — work required to fix once spotted

Closest to 'simple parameterised fix' (e3). The quick_fix is to replace == with === and add strict_types=1. This is a mechanical substitution, but it requires finding every loose comparison in security-sensitive paths across potentially multiple files — more than a single one-line patch but not a large-scale refactor. The pattern replacement is straightforward once identified.

b5 Burden Structural debt — long-term weight of choosing wrong

Closest to 'persistent productivity tax' (b5). The term applies_to web, cli, and queue-worker contexts — the full PHP application surface. Every developer touching authentication, token validation, or type-sensitive comparisons must carry the mental overhead of always preferring === and auditing in_array() calls. It's a pervasive productivity tax but doesn't define the entire system architecture.

t9 Trap Cognitive debt — how counter-intuitive correct behaviour is

Closest to 'catastrophic trap' (t9). The misconception field explicitly states developers believe type juggling only produces edge-case oddities, when in fact '0e123' == '0e5678' is true in authentication logic — a silent, exploitable security bypass. The 'obvious' approach of using == for equality is always wrong in security contexts, and the failure mode is a complete authentication bypass rather than a minor bug.

About DEBT scoring →

Also Known As

PHP type coercion loose comparison bug PHP == vulnerability

TL;DR

PHP's loose comparison (==) can produce unexpected results — "0e123" == "0e456" is true, enabling auth bypasses.

Explanation

PHP's loose comparison operator (==) performs type coercion before comparing. "Magic hash" strings starting with 0e followed by digits are treated as scientific notation floats — so "0e1234" == "0e5678" evaluates to true because both equal 0.0. This can bypass hash comparison checks if the comparison uses == instead of ===. Similarly, 0 == "any_string_not_starting_with_digit" is true in older PHP. Always use strict comparison (===) when comparing security-sensitive values.

How It's Exploited

POST role=0
# With loose ==, the string '0' == 0 == false == null — unintended matches abound

Common Misconception

PHP type juggling only produces odd results in edge cases. Using == instead of === in authentication logic can allow "0e123" to equal any other "0e" hash (both evaluate to 0 in scientific notation), enabling magic hash bypass attacks in login systems.

Why It Matters

PHP's loose comparison operator == performs type coercion before comparing — '0e1234' == '0e5678' is true because both are treated as scientific notation equal to zero, famously enabling hash bypass.

Common Mistakes

  • Using == to compare password hashes — magic hashes starting with 0e all equal each other.
  • Comparing user input to boolean true with == — any non-empty string equals true loosely.
  • Using in_array() without the strict third argument — in_array('0', [false]) returns true.
  • JSON-decoded values changing type compared to expected type — use === always for security comparisons.

Avoid When

  • Never use == for comparing passwords, tokens, or hashes — type juggling can make '0e123' == '0e456' true.
  • Avoid loose comparisons with in_array() — always pass true as the third argument for strict mode.

When To Use

  • Use strict comparison (===) for all security-sensitive checks — authentication, token comparison, type validation.
  • Enable strict_types=1 to enforce type safety at the function call level.

Code Examples

✗ Vulnerable
// PHP loose comparison — 0 == 'admin' is TRUE in PHP < 8
if ($_POST['role'] == 0) { grantAccess(); }

// Magic hash: md5('240610708') === '0e462097431906509019562988736854'
// 0e... == 0e... is TRUE (scientific notation comparison)
if ($hash == $storedHash) { ... }
✓ Fixed
// Always use strict comparison
if ($_POST['role'] === 0) { grantAccess(); }

// For hash comparison, use hash_equals() (also constant-time)
if (hash_equals($storedHash, $userHash)) { ... }

Added 15 Mar 2026
Edited 31 Mar 2026
Views 217
Rate this term
No ratings yet
🤖 AI Guestbook educational data only
| |
Last 30 days
2 pings T 1 ping W 3 pings T 0 pings F 0 pings S 0 pings S 0 pings M 0 pings T 1 ping W 2 pings T 4 pings F 2 pings S 9 pings S 1 ping M 1 ping T 0 pings W 0 pings T 1 ping F 0 pings S 1 ping S 0 pings M 1 ping T 0 pings W 1 ping T 1 ping F 2 pings S 1 ping S 0 pings M 3 pings T 0 pings W
No pings yet today
ChatGPT 3
ChatGPT 98 Perplexity 27 Amazonbot 20 Scrapy 19 Google 9 Ahrefs 7 Unknown AI 6 SEMrush 4 Claude 2 Majestic 1 Qwen 1 Meta AI 1 Sogou 1 PetalBot 1 Bing 1
crawler 187 crawler_json 8 pre-tracking 3
DEV INTEL Tools & Severity
🟠 High ⚙ Fix effort: Medium
⚡ Quick Fix
Use strict comparison === everywhere; enable strict_types=1; add type declarations to catch coercion bugs at analysis time
📦 Applies To
PHP 5.0+ web cli queue-worker
🔗 Prerequisites
🔍 Detection Hints
== comparison with mixed types; 0 == 'a' evaluates true; in_array without strict third argument
Auto-detectable: ✓ Yes phpstan psalm semgrep
⚠ Related Problems
🤖 AI Agent
Confidence: High False Positives: Medium ✓ Auto-fixable Fix: Low Context: Line Tests: Update
CWE-704 CWE-697


✓ schema.org compliant