Skip to content

VNX-PHP-007: PHP extract on superglobal

Detect request data imported as local variables: extract() of superglobals or request data, parse_str()/mb_parse_str() without a result array, $$key or $GLOBALS[$key] assignment in a foreach over a superglobal, and import_request_variables().

Updated 02 Nov 2026Checked against cli@de9b52a
Field Value
Rule ID VNX-PHP-007
Severity High
SARIF level error
Kind sast
Languages PHP
CWE CWE-621
CAPEC CAPEC-17
ATT&CK T1190
CWSS vector CWSS:1.0/TI:H/AP:A/AL:A/IC:N/FC:LT/RP:N/RL:A/AV:I/AS:N/IN:A/SC:A/BI:H/DI:H/EX:H/EC:N/P:H
Tags variable-overwrite injection php
Source internal/sast/rules/vnx-php-007.rego

Overview

This rule flags calls to PHP’s extract() function that operate directly on superglobals: $_GET, $_POST, $_REQUEST, $_COOKIE, $_SERVER, and $_FILES. The extract() function imports each key of an associative array as a variable into the current scope. When an attacker controls the keys, as they do with any superglobal, they can inject variables of any name and value into the function’s local scope, silently overwriting variables that were set before the call, including authentication flags, access control decisions, and database connection handles. This maps to CWE-621: Variable Extraction Error.

Why This Matters

Legacy PHP applications from the register_globals era (PHP 4 and early PHP 5) often used extract($_GET) or extract($_POST) at the top of pages as a convenience shortcut. This practice directly mirrors register_globals = On, a setting removed in PHP 5.4 specifically because it enabled exactly this class of attack. If a page does:

$isAdmin = false;
extract($_GET);
if ($isAdmin) { /* grant admin access */ }

An attacker simply requests ?isAdmin=1 and gains admin access. The attack works against any boolean flag, string credential, numeric threshold, or object reference that was set before the extract() call, regardless of how carefully those initial assignments were made.

The attack is equally effective against variables that influence SQL queries, file paths, or include statements, all of the variable-based vulnerabilities in this rule set can be enabled or worsened by a prior extract() call.

What Gets Flagged

Any construct that turns request fields into variables of the current scope.

// FLAGGED
extract($_POST);
extract($_REQUEST, EXTR_SKIP);          // still lets the client define unset variables
extract($request->all());
parse_str($_SERVER["QUERY_STRING"]);    // no result array
foreach ($_POST as $key => $value) {
    $$key = $value;
}

Not flagged: extract($_POST, EXTR_PREFIX_ALL, "form"); parse_str($qs, $params) with a result array; extract($data) of view data, database rows or parameters whose origin is not visible in the file; $zip->extract() and other methods; the names in comments and strings.

Remediation

  1. Replace extract() with explicit variable assignments. Assign only the specific keys you actually need, with validation on each value. This makes the data flow visible and prevents any unexpected variable injection:
// SAFE: explicit assignments, only the keys you intend, with validation
$username = filter_input(INPUT_POST, 'username', FILTER_SANITIZE_SPECIAL_CHARS) ?? '';
$page     = (int) ($_GET['page'] ?? 1);
$sort     = in_array($_GET['sort'] ?? '', ['asc', 'desc'], true) ? $_GET['sort'] : 'asc';
  1. Use filter_input() and filter_input_array() for validated access to superglobals. These functions apply type-casting and sanitization filters at the point of access, making it impossible to accidentally import unvalidated data:
// SAFE: filter_input_array with explicit field definitions and types
$inputs = filter_input_array(INPUT_POST, [
    'email'    => FILTER_VALIDATE_EMAIL,
    'age'      => FILTER_VALIDATE_INT,
    'redirect' => FILTER_SANITIZE_URL,
]);

$email    = $inputs['email']    ?? null;
$age      = $inputs['age']      ?? null;
$redirect = $inputs['redirect'] ?? '/dashboard';
  1. If you encounter extract() in legacy code you cannot fully refactor immediately, at minimum change the flag to EXTR_SKIP so that existing variables are never overwritten by incoming data, and combine it with a prefix to namespace imported variables:
// LESS BAD: EXTR_PREFIX_ALL prevents overwriting, but explicit assignment is still better
extract($_GET, EXTR_PREFIX_ALL, 'get');
// Variables become $get_username, $get_page, etc., original variables are untouched
  1. Search the entire codebase for extract( calls and review each one. Legacy codebases that used register_globals compatibility shims at the top of shared include files may have this pattern in common.php, init.php, or config.php, files that run on every page.

References

Suppressing this rule

Put a nosec comment on the line, or on line 1 to cover the whole file. Use the comment marker of the file’s language (#, //, -- or ;):

# nosec vnx-php-007
// nosec vnx-php-007

Or record an ignore rule: vulnetix ignore add --rule vnx-php-007 --reason "...". See Suppressing findings.

Edit this pageSources: cli/internal/sast/rules/vnx-php-007.rego, cli/internal/sast/docs/vnx-php-007.md
esc
↑ ↓ move↵ openSearches in your browser. A key like autonomy.max or a code like threshold_required jumps straight to it.