register_globals (Legacy Audit)
debt(d5/e7/b7/t7)
Closest to 'specialist tool catches' (d5), since semgrep/rector can flag uninitialized variable usage and extract($_REQUEST) patterns, but the symptom is silent in legacy code — variables just appear to work.
Closest to 'cross-cutting refactor across the codebase' (e7), because every variable usage that implicitly depended on globals must be rewritten to explicit $_GET/$_POST/$_COOKIE access throughout the legacy codebase.
Closest to 'strong gravitational pull' (b7), since register_globals reliance shapes how every script reads input — applies_to web context broadly, and removing it touches the entire request-handling surface.
Closest to 'serious trap' (t7), per the misconception: developers mistake it for a minor convenience when it's actually a system-wide injection vector; the 'compatibility shim' extract($_REQUEST) recreates the exact vulnerability.
Also Known As
TL;DR
Explanation
register_globals, deprecated in PHP 5.3 and removed in PHP 5.4, caused all GET, POST, and COOKIE parameters to be injected as global variables. This meant a URL like ?isAdmin=1 would set $isAdmin = true in any script, trivially bypassing authentication. The setting is long removed, but legacy codebases running on outdated PHP versions or those migrated from old code may still contain logic that assumed register_globals behaviour. When auditing legacy PHP, check for uninitialised variable usage and assume any global variable may be attacker-controlled.
Common Misconception
Why It Matters
Common Mistakes
- Legacy code that relied on register_globals and was 'fixed' by adding extract($_REQUEST) — same vulnerability.
- Not auditing old codebases for implicit reliance on register_globals before upgrading PHP.
- Using extract() on user input as a compatibility shim — replicates the vulnerability exactly.
- Not understanding that modern code using proper $_GET/$_POST access is the secure replacement.
Code Examples
// register_globals equivalent — extract() on user input:
extract($_REQUEST); // ?admin=1 creates $admin = '1'
if ($admin) grantAccess(); // Bypassed via URL parameter
// Safe equivalent:
$admin = $_SESSION['is_admin'] ?? false; // Server-controlled, not user-supplied
; register_globals removed in PHP 5.4 — but know what it did:
; GET ?admin=1 would automatically create $admin = 1
; This was a catastrophic security hole
; Modern PHP — always read from superglobals explicitly:
$admin = (int) ($_GET['admin'] ?? 0);
$name = trim( $_POST['name'] ?? '');
$token = (string) ($_COOKIE['tok'] ?? '');
; Validate every superglobal value — never trust raw input:
$id = filter_input(INPUT_GET, 'id', FILTER_VALIDATE_INT);
if (!$id) abort(400);