Variablen in PHP sauber übergeben mit register_globals=off

, Stand: 16.03.2012  Tipp druckenDrucken Symbol Kommentare 8 Kommentare

Einst war es zu einfach, in PHP unsicheren Code zu schreiben. Das hat sich mit PHP 4.1 geändert.

Wichtigste Neuerung von PHP 4.1 war das neue Übergabeverfahren für Userdaten von Formular oder Cookie zum Programm. Bislang setzt aber kaum ein Provider den Wert register_globals auf off. Doch es lohnt sich dennoch, umzusteigen. Denn mit der neuen Art der Variablenübergabe kann man sich eine Menge Ärger sparen.

Dieser Beitrag zeigt, wie man damit umgeht.

Neues Übergabeverfahren für Userdaten

PHP hat eine angenehme Eigenschaft: Per GET oder POST aus Formularen übergebene Werte gelten im aufgerufenen PHP-Programm automatisch als globale Variablen. Das bedeutet: Theoretisch lassen sich über jeden Aufruf eines PHP-Programms in der URL beliebige Variablen ins Programm schmuggeln. Das mag zwar meist erwünscht sein, führt andererseits bei unsauberer Programmierung schnell zu Problemen.

Das folgende Beispiel verdeutlicht die Schwachstellen. Ein Formular ruft die Datei global.php auf und übergibt ihr per POST den Inhalt des Eingabefeldes als Variable hallo.

<form action="global.php" method="POST">

<input type="text" name="hallo"></input>

<input type=submit>

</form>

Das Programm global.php enthält gerade einen Befehl:

<?

echo $hallo;

?>

Ein Klick auf den Submit-Button übergibt hallo aus dem Formular ins Programm. global.php zeigt brav den Wert der Variablen. Doch hier sitzt eine Sicherheitslücke sein: Denn genauso kann jemand die Datei mit http://foo.bar/global.php?hello=Oh+ich+Hacker aufrufen und Daten manipulieren.

Besonders nachlässige Programmierer haben dann ein Problem, wenn Sie in der verarbeitenden Datei keine Prüfroutine vor die Verwendung der Variablen geschaltet haben. Auf diese Weise lassen sich beispielsweise ganz einfach Javascript-Prüfroutinen in Formularen aushebeln.

Schlimmeres droht, wenn in IF-Konstruktionen Variablen gesetzt werden und wenn diese Variablen vorher nicht sauber initialisiert sind. Ein Beispiel:

<?

if (1 == 2)

{

$wichtig = 10;

}

echo $wichtig;

?>

In diesem Beispiel ist bewusst eine unmögliche Bedingung eingebaut. Das if kann in keinem Fall wahr sein, die Variable $wichtig bleibt ohne Wert. Ruft aber jemand diese Datei mit beispiel.php?wichtig=10 auf, erhält die Variable trotzdem einen Wert.

Natürlich sind solche Sicherheitslücken nicht Schuld der globalen Registrierung von Variablen. Aber die Registrierung erleichtert das Entstehen solcher Fehler. Das soll sich alles ändern. PHP 4.1 will Fehlerquellen ausschließen und bietet einen neuen Eingabemechanismus an. „Bietet an“ bedeutet: Noch ist die globale Registrierung der übergebenen Variablen nicht ausgeschaltet. Und das neue Verfahren soll das alte nur ergänzen, jedoch nie verdrängen.

Also Entwarnung: Niemand muss sofort Code umschreiben. Allerdings soll mit der nächst größeren Version der Eintrag register_globals in php.ini per Default deaktiviert sein. Sprich: wer mit einem späteren PHP einsteigt, wird zum neuen Verfahren erzogen.

Das neue Übergabeverfahren besteht aus einem Satz von speziellen Arrays. Je nach Art der Datenübergabe erhält das entsprechende Array die Daten.

Die Arrays sind:

$_GET - für Formulareinträge, die per GET, also über die URL weiter gegeben werden

$_POST - für Formulareinträge, die per POST, also für den Nutzer unsichtbar übergeben werden

$_COOKIE - für Variablen die aus Cookies kommen

$_SERVER - für Servervariablen

$_ENV - für Umgebungsvariablen

$_REQUEST - speichert die per GET, POST und in COOKIES übergebenen Daten. Das ist der flexibelste Weg, vom User übergebene Daten einzulesen.

$_SESSION - enthält HTTP-Session-Variablen aus den Modulen

Für den Programmierer bedeutet das: eine übergebene Variable steht nicht sofort global zur Verfügung. Um die Variable ins Programm zu übernehmen muss man sie aus dem Array fischen. Allein dieser Schritt bringt viel Sicherheit. Denn der Programmierer muss bestimmen, woher das Programm die Variable und wo ihr Inhalt schließlich landet. Ein Beispiel:

$my_hallo = $_POST["hallo"];

echo $my_hallo;

Hier wird die Variable hallo nur dann angenommen und $my_hallo zugewiesen, wenn hallo aus einem POST-Formular kommt. Das schließt alle Hack-Versuche über die URL aus. Probieren Sie es selbst: Schreiben Sie den Einzeiler an Stelle des echo $hallo in die eingangs verwendete Beispieldatei global.php. Der Aufruf über das Formular wird weiter funktionieren, mit http://foo.bar/global.php?hello=Oh+ich+Hacker hingegen ist nichts mehr zu machen.

Das Array auslesen

Das Plus an Sicherheit bei der Datenübergabe kostet nur wenig mehr Programmieraufwand. Es gilt, die Daten aus dem Array zu lesen und den Variablen zu übergeben. Dazu gibt es mehrere Ansätze.

Voreweg: Um Programmierern die Umstellung und das Umschreiben ihres Codes zu erleichtern, sind die Arrays von Haus aus global. Sie können also auch ohne explizites global in jeder Funktion eingesetzt werden. Damit will das PHP-Entwicklerteam die Umstellung erleichtern. In vielen Fällen genügt es, die einst Variablen einfach zu ersetzen.

Der einfachste Weg, an die Daten aus dem Array zu kommen ist der direkte, wie diese Zeile zeigt:

echo $_POST["hallo"];

Der Befehl gibt den Inhalt der via POST übergebenen Variablen hallo aus. Das jedoch ist nur in wenigen Fällen sinnvoll. Soll eine Variable im Programm mehrfach genutzt und bearbeitet werden, sollten Sie den Inhalt aus dem Array in die Variable kopieren. Dabei bietet es sich an, bei eventuell fehlenden Variablen einen Default-Wert zu setzen:

if (! ($my_hallo=$_POST["hallo"]))

{

$my_hallo="Defaultwert";

}

echo $my_hallo;

Die if-Anweisung überprüft das Gelingen der Zuweisung von aus dem Array an die Variable. Schlägt dies fehl, sprich, existiert der Schlüssel hallo nicht, erhält $my_hallo einen Default-Wert.

Wie sich hier zeigt, sind die bei der Datenübergabe verwendeten Arrays assoziativ: Im Array sind die übergebenen Variablen als Schlüssel und die zugehörigen Werte als Werte gespeichert liegen. Damit lassen sich beliebige Namen-Werte-Paare an ein Programm übergeben.

Folgendes Code-Beispiel zeigt, wie Sie alle in einem Array übergebenen Werte anzeigen:

<?

echo "Per POST übergebene Variablen:<br>\n";

while (list ($key, $val) = each ($_POST))

{

echo "$key => $val<br>";

}

?>

Die while-Schleife liest den übergebenen Array Eintrag für Eintrag aus. Mit list erhält jeweils die Variable $key den Schlüssel oder Namen des Eintrages und $val dessen Wert. Dieses Verfahren eignet sich nicht unbedingt für den Betrieb auf einer Web-Seite. Aber bei der Fehlersuche ist diese Schleife sinnvoll - sie hilft herauszufinden, ob alle Variablen richtig übergeben sind.

Gekoppelt mit einem Filter allerdings bringt list mehr Leistung. Um bestimmte Einträge aus dem Array heraus zu filtern, wäre ein Weg wie dieser denkbar:

<?

echo "Per POST übergebene Variablen:<br>\n";

while (list ($key, $val) = each ($_POST))

{

switch ($key)

{

case "name":

$name = $val;

break;


case "vorname":

$vorname = $val;

break;


case "alter":

$alter = $val;

break;

default:

echo "Array-Eintrag $key = $val nicht zulässig<br>\n";

}

}

echo "Die Variablen: \$name: $name, \$vorname: $vorname, \$alter: $alter<br>\n";

?>

Hier nimmt ein Switch-Statement die Wertepaare auseinander. Nur wenn ein bestimmter Eintrag in $key vorhanden ist, wird er in die gleichnamige Variable übernommen. Trifft bei einem Eintrag keiner der Fälle zu, landet das Programm in default: und der Eintrag nebst Inhalt werden als unzulässig gemeldet. Beim Debugging ist dies eine große Hilfe. Ob man aber einem digitalen Einbrecher diese Hilfe in Form der Fehlermeldung zugesteht, sollte wohl überlegt sein.

Ein weiterer sehr einfacher aber unsicherer Weg führt über die Funktion extract. Der folgende Code-Ausschnitt zeigt, wie es geht:

<?

echo "Per POST übergebene Variablen ";

echo "mit extract ausgewertet:<br>\n";

extract ($_POST);

echo "Die Variablen: \$name: $name, \$vorname: $vorname,

\$alter: $alter, \$sonstwas: $sonstwas<br>\n";

?>

extract nimmt die Schlüssel-Wert-Paare und wandelt jeden einzelnen Eintrag in eine Variable mit entsprechendem Wert um. Allerdings entziehen Sie sich selbst der vollen Kontrolle über das Programm. Denn es ist auf diesem Weg wieder möglich, unerwünschte Variablen in das Programm zu schmuggeln.

Als Gegenmittel bietet extract einige Parameter. Mit

extract ($_POST, ENTR_SKIP)

verhindern Sie, dass eine bereits bestehende Variable von der übergebenen überschrieben wird. Das setzt eine saubere Initialisierung der Variablen voraus. Ein anderer Weg wäre, den eingelesenen Variablen erst ein Mal ein Präfix voranzustellen und sie dann auszuwerten. Das geht so:

extract ($_POST, EXTR_PREFIX_ALL, "neu")

Aus einer per POST übergebenen Variable name wird mit diesem Befehl die Variable $neu_name.

Wenn Sie die Programmbeispiele in diesem Abschnitt ausprobieren wollen, hilft wieder ein einfaches Formular. Um eine Fehlermeldung zu provozieren, ist der letzte Formulareintrag namens sonstwas dabei:

<form action="array_switch.php" method="POST">

<input type="text" name="name"></input><br>

<input type="text" name="vorname"></input><br>

<input type="text" name="alter"></input><br>

<input type="text" name="sonstwas"></input><br>

<input type=submit>

</form>

Setzen Sie in action jeweils den Namen des aufzurufenden Programms ein.

Probieren Sie das neue Übergabeverfahren aus. Und verwenden Sie es am besten in allen neuen Projekten. Für geringen Mehraufwand belohnt das neue Verfahren mit deutlich mehr Sicherheit.

Kommentare 0 bis 5 von 8 Kommentaren   >>

Hi Leute,
mal vorab: Ich bin blutiger PHP-Anfänger!
Ich habe den Auftrag erhalten, auf einer html-Seite eine kleine
Produktzusammenstellung zu machen. Dabei habe ich jedem Produkt einen
Namen gegeben und einen Preis, die dann beim Bestellvorgang wieder
abgefragt werden! Nun habe ich obigen Artikel gelesen und mir ist
aufgefallen, dass der Preis oder auch der Name bei der übergabe per
URL einfach geändert werden kann! Wie kann ich diese Lücke
schließen und die Übergabemethode verändern?
Zur Verdeutlichung: www.c-mueller-online.de/Shop/blumenkasten.htm
(dies ist die Seite mit den Produkten)
www.c-mueller-online.de/Shop/bestellvorgang.php (dies ist das script
mit dem Namen und Preis abgefragt werden)
Ich wäre sehr dankbar, wenn mir jemand helfen könnte! Am besten
gleich durch einen Scriptvorschlag! :-)
Vielen Dank
[Sascha Müller | 05.09.2007] Antworten

Nachdem diese Seite wohl ganz gerne verlinkt wird und Anfänger
wahrscheinlich verwirrt werden, vielleicht mal eine Klarstellung:
register_globals auf "on" kann erwiesenermaßen zu unsicherem Code
führen. Deshalb sollte man register_globals immer auf "off" belassen.
So weit so gut.
Wenn man allerdings - wie von den Vorkommentatoren vorgeschlagen - die
Werte aus $_GET und $_POST wieder in seinen globalen Namensraum
importiert, fängt man sich im Zweifel genau diesselben
Sicherheitslücken ein, die man vorher umgehen wollte.
Ich halte das deshalb für einen echten Schildbürgerstreich.
[Bastian Frank | 15.12.2006] Antworten

Respekt! Ist wirklich eine sehr gute Beschreibung.
[george-lehmann | 02.09.2005] Antworten

Heiko: Sorry, das Kommentarmodul filtert alles heraus, was nach Tag
aussieht. Vorschlag: Schau doch mal ins Forum (Link ist links in der
Rubrik "Service") Dort kannst Du das Script in einem neuen Thread
posten.
[Martin Goldmann | 29.09.2004] Antworten

Hallo,
ich habe mir noch etwas zum Thema überlegt.
Die Grundüberlegung:
1. Eine bereits deklarierte Variable darf nicht überschrieben werden.
2. POST geht vor GET.
Probiert doch bitte mal das folgende Script aus und sagt mir Eure
Meinung.
Achtung: Das zweite Parameter kann verändert werden - ist im Script
beschrieben:
[code]
[/code]
[Heiko Thoms | 29.09.2004] Antworten

Kommentare 0 bis 5 von 8 Kommentaren   >>

Ihr Kommentar:


Name:


Aktualisiert am: 16.03.2012