PHP ohne "register_globals"

"register_globals" ist eine Konfigurationsoption für die Skriptsprache PHP. Sie steuert, wie PHP mit POST und GET Variablen des HTTP-Headers umgeht. Die Verwendung dieser Option ist bei unsauberer Programmierung ein Sicherheitsproblem und wird deshalb in Zukunft nicht mehr unterstützt. Lernen Sie, wie Sie Ihre Programme anpassen und das Problem vermeiden können.

 

 

 

1. Was bewirkt "register_globals"?
2. Was ist gefährlich daran?
3. Sicherer Code trotz aktiviertem register_globals?
4. Der richtige Weg ohne register_globals
5. Hinweis zu den Code-Beispielen


1.  Was bewirkt "register_globals"?
Bis zur Version 4.2 von PHP wurden Variablen, welche die Web-Browser per POST und GET übermittelt haben, von PHP automatisch als globale Variablen registriert. Im folgenden Beispiel werden dem PHP Skript unter http://example.com/login1.php die beiden Variablen username und password per HTTP GET übergeben:

Der HTTP Request

http://example.com/login1.php?username=foo&password=bar

mit folgendem Beispielcode in der Datei login1.php

<?php
echo "username: $username";
echo "password: $password";
?>

würde folgende Ausgabe erzeugen:

username: foo
password: bar

Die Variablen $username und $password wurden ohne weiteres Zutun des Programmierers vom HTTP-Request des Browsers definiert und deren Werte übernommen. Soweit scheint dieses Beispiel auf den ersten Blick für "faule" Programmierer ganz praktisch.


2 Was ist gefährlich daran?
Ein supponierter entfernter Angreifer kann bei aktivierter Option register_globals jedoch mit seinem Browser beliebige Variablen übermitteln und damit für das PHP Skript definieren. Wie fatal dies sein kann, zeigt das folgende, schlechte, Beispiel:

login2.php:

<?php
if(verify_userpass($username,$password)){
$authenticated=true;
}

if($authenticated==true){
require_once("top-secret.inc");
}
?>

verify_userpass() sei eine Funktion, welche Username und Passwort überprüft und bei Erfolg true zurück gibt. Die erste if-Anweisung lässt die Variablen $username und $password überprüfen. Wenn die Daten stimmen, definiert sie die Variable $authenticated und setzt deren Wert auf true, um zu signalisieren, dass der Benutzer authentifiziert ist. Schlägt die Überprüfung von Username und Passwort fehl, wird die Variable $authenticated nicht gesetzt und der Code in der zweiten if-Anweisung wird nicht ausgeführt. Der Programmierer hat damit vermeintlich sein Ziel, Unbefugten das Ausführen des Codes in top-secret.inc zu verwehren, erreicht.

Wenn der Angreifer aber von der Existenz der Variable $authenticated Kentniss hat, kann er die Überprüfung ausser Kraft setzen indem er $authenticated einfach gleich selbst mitliefert:

http://example.com/login2.php?username=foo&password=bar&authenticated=true

Durch die aktivierte Option "register_globals" registriert nun PHP automatisch die vom Browser (zusätzlich) gelieferte Variable $authenticated und gibt ihr den Wert "true". Die Überprüfung des Usernamen und des Passwortes schlägt zwar wie vorgesehen fehl, da aber der Programmierer die Variable $authenticated verwendet, ohne sie zu Beginn zu initialisieren, trifft die Bedingung in der zweiten if-Anweisung trotzdem zu. Damit hat der Angreifer den Zugriffsschutz erfolgreich ausgehebelt.

Durch die Eigenschaft von PHP, nicht existente Variable bei Ihrer ersten Verwendung fraglos anzulegen, würde sogar folgende URL ausreichen um einzudringen:
http://example.com/login2.php?authenticated=true


3 Sicherer Code trotz aktiviertem register_globals?
Damit das Skript in dieser Art trotzdem sicher funktioniert, hätte der Programmierer einfach alle Variablen, welche nicht vom Browser übergeben werden dürfen, vor der ersten Verwendung initialisieren müssen:

login3.php:

<?php
// initialize non user-supplied variables:
$authenticated=false;

if(verify_userpass($username,$password)){
$authenticated=true;
}

if($authenticated==true){
require_once("top-secret.inc");
}
?>

In login3.php registriert PHP zwar eine vom Angreifer übergebene Variable $authenticated. Der Programmierer hat die Gefahr aber erkannt und überschreibt deren Inhalt beim initialisieren mit false. Damit ist dieser Angrif abgewehrt.

Leider haben viele Programmierer bekannter PHP-Software nachlässig programmiert und dies nicht berücksichtigt. Gerade bei grösseren Projekten ist es zudem schwer, die Übersicht über alle Variable zu behalten. Zu leicht schleicht sich ein Fehler ein, welcher beim bekannt werden in allen Installationen der Software ein grosses Sicherheitsloch aufreisst. Das PHP-Projekt hat aus diesem Grund "register_globals" ab PHP Version 4.2 standardmässig abgeschaltet und wird die Möglichkeit die gefährliche Option zu aktivieren ab Version 6 gar nicht mehr bieten.


4 Der richtige Weg ohne register_globals
Der vom PHP-Projekt vorgesehen Weg, auf die POST und GET Variablen zuzugreifen, führt über die Superglobalen Arrays $_POST und $_GET. Die beiden Arrays enthalten die Elemente welche vom Browser entweder per HTTP-POST oder -GET übermittelt wurden. Damit gibt es keine "Kollisionen" mit normalen Programm-Variablen mehr.

Der Zugriff auf den Inhalt erfolgt mit $_GET["username"], falls die Variable "username" per GET übermittelt worden sind, und per $_POST["username"] für HTTP-Posts. Das obige Beispiel anzupassen, verursacht kaum Aufwand:

login4.php:

<?php
// initialize all variables:
$authenticated=false;
$username=$_GET["username"];
$password=$_GET["password"];

if(verify_userpass($username,$password)){
$authenticated=true;
}

if($authenticated==true){
require_once("top-secret.inc");
}
?>

Damit ist die Potentielle Lücke durch vergessen gegangene Initialisierung geschlossen. login4.php wäre bei abgeschaltetem register_globals sogar ohne die Initialisierung von $authenticated sicher.


5 Hinweis zu den Code-Beispielen
Die Code-Beispiele sind der Übersicht halber bewusst einfach gehalten und eignen sich in dieser Art nicht für die direkte Verwendung in der Praxis!