PHP lernen (3): Interaktive Webseiten gestalten
Querystrings
Querystrings
Auf dieser Seite
Mit Bild
Ganz kurz haben Sie oben schon gesehen, wie Daten an einen URI gehängt werden. Dabei hängen Sie an den URI mittels Fragezeichen einen sogenannten Querystring an:
http://www.domain.de/beispiel.php?variable=wert
Mit Bild
Mit diesem Link wird die Datei beispiel.php aufgerufen. In dieser Datei steht nun die Variable mit Namen variable und dem Wert wert zur Verfügung. Sie sprechen die Variable über das Array $_REQUEST oder $_GET an. Schreiben Sie folgenden Code in eine Datei, die Sie als beispiel.php abspeichern:
Mit Bild
<?php print $_GET["variable"]; // Ausgabe: wert ?>
Mit Bild
Wenn Sie diese Datei über den oben angegebenen Link mit Querystring aufrufen, tut die Datei nichts weiter als den Wert des Querystrings ausgeben. Wir wollen ein wenig mit Querystrings experimentieren. Schreiben Sie sich dazu ein Skript, das im Body-Tag zuerst das Array $_GET komplett ausgibt und darunter schreiben Sie einen Link:
Mit Bild
<? print '<pre>'; print_r($_GET); print '</pre>'; ?> <a href="php1_w3_02.php?variable=wert">Dies ist ein Link</a>
Mit Bild
Speichern Sie das Skript unter dem Namen php1_w3_02.php. Wenn Sie das Skript zum ersten Mal aufrufen, wird $_GET leer sein. Klicken Sie nun auf den Link, wird das gleiche Skript aufgerufen, im Array $_GET steht aber die Variable $_GET['variable'].
Mit Bild
Sie können auch mehrere Variablen in einen Querystring schreiben:
Mit Bild
<a href="beispiel.php?variable1=wert1&variable2=wert2">mehrere Variablen</a>
Mit Bild
Übertragen werden sowohl Zeichenketten also auch Zahlen. Als Datentypen erkennt PHP automatisch eine Zeichenkette.
Mit Bild
Übertragung von Arrays im Querystring
Arrays können Sie so nicht übertragen. Wenn Sie Arrays per Querystring übertragen möchten, hängen Sie jedes Element des Array einzeln an. Verwenden Sie für den Variablennamen die eckigen Klammern, dann erkennt PHP bei der Entgegennahme gleich wieder das Array:
Mit Bild
<a href="beispiel.php?variable[]=wert1&variable[]=wert2">Array</a>
Mit Bild
Diese Variablen werden von PHP als Array entgegen genommen. $_GET["variable"] ist nun ein Array, keine skalare Variable mehr. Verwenden Sie zum Beispiel einen Index, um die einzelnen Elemente auszugeben:
Mit Bild
<?php print $_GET["variable"][0]; // Ausgabe: wert1 print '<br>'; print $_GET["variable"][1]; // Ausgabe: wert2 ?>
Mit Bild
URL Kodierung
Wenn Sie Zeichenketten über die URL weiterreichen, sollten Sie grundsätzlich in Erwägung ziehen, diese zu kodieren. Wenn Sie zum Beispiel Benutzereingaben über die URL transportieren, dann können Sonderzeichen in der Zeichenkette stehen. Solche Sonderzeichen müssen für die URL kodiert werden. Die meisten Browser machen das selbstständig, Sie können sich aber nicht darauf verlassen, wie die Zeichenketten kodiert werden und ob sie wieder korrekt dekodiert werden. Machen Sie es also im Zweifelsfall selbst und gezielt mit den PHP-Funktionen rawurlencode() und rawurldecode(). Setzen Sie in den Wert ein Leerzeichen, um die Kodierung sichtbar zu machen:
Mit Bild
<a href="php1_w3_02.php?kodiert=<?=rawurlencode ('wert 1');?>"> kodiert</a>
Mit Bild
Wenn Sie auf diesen Link klicken, sehen Sie in der Adresszeile des Browsers einen Link in dieser Art:
Mit Bild
http://localhost/Workshop/Woche3/php1_w3_02.php?kodiert=wert%201
Mit Bild
Das Leerzeichen wurde durch %20 ersetzt. In der Ausgabe wird Ihr Browser wahrscheinlich automatisch das %20 wieder dekodieren. Im Zweifel dekodieren Sie die Zeichenkette selbst mit rawurldecode():
Mit Bild
print rawurldecode($_GET['kodiert']);
Mit Bild
Wenn Sie dagegen genau wissen, was in den Werten des Querystrings steht, weil Sie ihn selbst generieren, brauchen Sie die Kodierung gegebenenfalls nicht. Sobald jedoch Leer- oder Sonderzeichen im Querystring stehen können, sollten Sie die Zeichenketten kodieren.
Mit Bild
Keine sensiblen Daten im Querystring!
Da der Querystring für jeden Benutzer offen zugänglich ist, sollten hier keine sensiblen Daten weitergereicht werden. Passwörter gehören niemals in den Querystring! Der Rechner des Benutzers könnte von Dritten eingesehen werden, der über den Verlauf im Webbrowser die besuchten Webseiten samt Querystring einsehen kann. Steht der Rechner in einem Netzwerk, kann der Netzwerkverkehr von Unbefugten abgehört werden. Dabei sind URIs mit angehängtem Querystring ebenfalls sichtbar.
Mit Bild
Beispiel: Hintergrundfarbe
Zur Veranschaulichung sehen Sie hier ein einfacheres Beispiel, das eine solche Variable verwendet, um die Anzeige der Hintergrundfarbe zu steuern. Verwenden Sie die Variable dabei direkt, kann ihnen etwas untergeschoben werden. Schreiben Sie in eine Datei php1_w3_03.php folgenden Code:
Mit Bild
<html> <head></head> <body bgcolor="<?=$_GET["bg"];?>"> <a href="php1_w3_03.php?bg=red">rot</a><br> <a href="php1_w3_03.php?bg=green">grün</a> </body> </html>
Mit Bild
Rufen Sie diese Datei nun zum Beispiel so auf: http://localhost/Workshop/php1_w3_03.php. Klicken Sie auf einen der Links. Sie sehen den Querystring in der Adresszeile offen vor sich liegen und können ihn als Benutzer natürlich manipulieren. Sie wollen einen gelben Hintergrund? Ändern Sie einfach die Adresszeile:
http://localhost/Workshop/php1_w3_03.php?bg=yellow
Das ist nicht weiter schlimm, weil der Benutzer hier nur die eigene Anzeige manipuliert. Einfluss auf die Anzeige der Webseite für Dritte hat er hiermit nicht. Dennoch wollen wir nur bestimmte Werte zulassen.
Die Schließung dieser Sicherheitslücke erfolgt durch Prüfung der Variablen auf ihren erlaubten Wert. Hier könnte man prüfen, ob die Variable entweder den Wert red oder den Wert green hat, und nur dann die Übergabe in das HTML-Attribut bgcolor erlauben:
Mit Bild
<? if ($_GET["bg"] == "red" || $_GET["bg"] == "green") { $bg = $_GET["bg"]; } else { $bg = "white"; } ?> <body bgcolor="<?=$bg;?>">
Mit Bild
Hat die Variable einen anderen Wert, wird die Hintergrundfarbe auf weiß gesetzt. Wenn Sie mehr als zwei mögliche Werte überprüfen, wird das schnell unkomfortabel. Arbeiten Sie dann mit einem Array, das die erlaubten Werte enthält. Die folgende Variante ist auch leichter erweiterbar.
Mit Bild
<? $arrErlaubteWerte = array("red", "green"); if (in_array($_GET["bg"], $arrErlaubteWerte)) { $bg = $_GET["bg"]; } else { $bg = "white"; } ?> <body bgcolor="<?=$bg;?>">
Mit Bild
Die Funktion in_array() prüft, ob ein bestimmter Wert in einem Array vorhanden ist. Wenn nun der Querystring unerlaubt manipuliert wird, wird die Hintergrundfarbe einfach auf weiß gestellt. Wollen Sie weitere Farben erlauben, ergänzen Sie diese einfach in Ihrem Array.
Mit Bild
Sie vergeben hier für $bg entweder den einen oder einen anderen Wert. Das können Sie mit einer if-else-Anweisung schreiben oder in der syntaktischen Kurzform:
$bg = (in_array($_GET["bg"], $arrErlaubteWerte)) ? $_GET["bg"] : "white";
Die Syntax folgt dem Muster:
(Ausdruck) ? true : false;
// (wenn das wahr) ? tue dies : sonst tue das;
// bzw.
$variable = (Ausdruck) ? true : false;
// $variable = (wenn das wahr) ? dieser Wert : sonst anderer Wert;
Diese Schreibweise bietet sich immer dann an, wenn eine if-else-Anweisung mit je einer Anweisung benötigt wird.
Mit Bild
Wie bereits erwähnt, wäre in diesem Beispiel eine Manipulation des Querystrings nicht weiter schlimm, weil sie keinen Einfluss auf die Anzeige der Webseite für Dritte hätte. Wie gefährlich solche Manipulationen im Zweifel sein können, zeigt das nächste theoretische Beispiel. Greifen wir ein wenig auf eine Datenbankanwendung vor.
Mit Bild
Beispiel: ID für Datenbankabfrage
Wenn Sie einen bestimmten Datensatz aus der Datenbank anzeigen wollen, übergeben Sie in einer Übersicht mit dem Link häufig die ID dieses Datensatzes. Der Redakteur oder Benutzer kann dann einen bestimmten Datensatz auswählen und anschließend bearbeiten oder löschen. Eine solche Liste sieht im Endergebnis in etwa so aus:
Mit Bild
<a href="news.php?id=1">erste News</a><br> <a href="news.php?id=2">zweite News</a><br>
Mit Bild
In der Adresszeile des Browsers steht nach Klick auf einen der Links:
http://localhost/news.php?id=1
Aus dieser ID generiert das Skript in der Datei news.php eine Datenbankabfrage, die den betreffenden Datensatz aus der Datenbank abfragt. Die Syntax für diese Abfrage lautet:
Mit Bild
SELECT news FROM newstabelle WHERE id = 1
Mit Bild
Aus dem Querystring könnte mit PHP die Datenbankabfrage generiert werden:
Mit Bild
$sql = 'SELECT news FROM newstabelle WHERE id =' . $_GET["id"];
Mit Bild
Hier würde die ungeprüfte ID an die Datenbank geschickt werden. Tun Sie das niemals! Der offen sichtbare Querystring kann leicht manipuliert werden. Ein böswilliger Besucher könnte den Befehl, die gesamte Tabelle zu löschen, an die ID anhängen:
http://localhost/news.php?id=1;DELETE+FROM+newstabelle
Einzelne SQL-Anweisungen werden mit Semikolon getrennt. Im ungünstigsten Fall könnte das Skript in der Datei news.php aus diesem Querystring die folgende SQL-Anweisung generieren:
Mit Bild
SELECT news FROM newstabelle WHERE id = 1; DELETE FROM newstabelle
Mit Bild
Zugegeben, man muss dafür erst einmal den Tabellennamen herausfinden. Ein Blindversuch könnte aber von Erfolg gekrönt sein und fatale Folgen haben: Würden diese Anweisungen an die Datenbank geschickt werden, wäre die Tabelle newstabelle hiernach unwiederbringlich leer!
Selbst wenn dieser Fall nicht einträte, würden unter Umständen hässliche Fehlermeldungen ausgegeben, die peinlich und sicher nicht im Sinne des Webseitenbetreibers sind.
Überprüfen Sie also den Wert der übergebenen Variablen. Erlaubt sind in diesem Fall ausschließlich numerische Werte. Diese Prüfung kann mit Regulären Ausdrücken (dazu mehr im nächsten Kapitel) erfolgen oder mit der Funktion is_integer(), die den in den Klammern übergebenen Parameter darauf prüft, ob er eine Ganzzahl ist.
Oder Sie zwingen mindestens den Wert in den Integertyp, siehe Typen-Tricks im Handbuch. Andere Zeichen als Ziffern würden dann entfernt:
Mit Bild
$sql = 'SELECT news FROM newstabelle WHERE id =' . (int)$_GET["id"];
Mit Bild
Bei einem Manipulationsversuch könnte so im schlimmsten Fall kein Datensatz ausgelesen werden, wenn eine News mit dieser ID nicht existiert oder wenn die Konvertierung nach integer 0 ergeben hat, weil die Zeichenkette nicht mit einer Ziffern begann. Eine leere Datenbankabfrage werden Sie sowieso abfangen, insofern wäre das undramatisch.
Dieses Beispiel werden Sie besser verstehen, wenn Ihnen der Umgang mit Datenbanken geläufiger ist. Es zeigt jedoch so schön einleuchtend, was bei Unachtsamkeit des Programmierers passieren kann.
Mit Bild
Beispiel: Inhalte über Querystring steuern
Ebenfalls schlimm sieht es aus, wenn Sie den Inhalt einer Seite über den Querystring steuern. Eigentlich eine komfortable Konstruktion, wenn man die externen Daten überprüft. Im Body könnte folgender Code stehen:
Mit Bild
<a href="php1_w3_test03.php?datei=inhalt_A.inc.php">Inhalt A</a><br> <a href="php1_w3_test03.php?datei=inhalt_B.inc.php">Inhalt B</a> <? if (isset($_GET["datei"])) { include($_GET["datei"]); } ?>
Mit Bild
Beachten Sie, dass dieses Skript und die folgenden eine PHP-Fehlermeldung erzeugen werden, solange die Dateien inhalt_A.inc.php, inhalt_B.inc.php und home.inc.php nicht im gleichen Verzeichnis wie das Skript existieren. Wenn Sie dieses Beispiel nachvollziehen wollen, dann legen Sie drei leere Dateien an, in die Sie der Übersicht halber lediglich die Namen der Dateien schreiben. Sie sehen dann gleich, welche der Dateien geladen wurde.
Mit Bild
Es wird lediglich geprüft, ob die Variable $_GET["datei"] existiert, weil PHP sonst bei include() ohne Parameter eine Fehlermeldung ausgeben würde. Dann wird der Inhalt der Variablen ohne weitere Prüfung in das Programm geladen. Auch hier kann der offene Querystring wieder leicht manipuliert werden:
http://localhost/php1_w3_test03.php?datei=http://www.akademie.de
http://localhost/php1_w3_test03.php?datei=etc/passwd
Der erste URI entführt den Benutzer, der zweite URI mit seiner relativen Pfadangabe lädt die Anzeige einer Passwortdatei vom Webserver. Hierin sind alle Passwörter aller Benutzer dieses Webservers abgelegt! Der Querystring könnte auch auf eine bösartige Datei verweisen, die Ihre Webseite, die Datenbank oder auch den gesamten Webserver ausspioniert.
Auch hier hilft es, wenn Sie - wie im Beispiel mit den Hintergrundfarben - ganz einfach die Variable aus dem Querystring auf erlaubte Werte überprüfen. Schreiben Sie die erlaubten Dateien in ein Array und überprüfen Sie, ob der per Querystring übergebene Wert im Array enthalten ist. Dieses Mal gehen wir sogar noch ein wenig weiter, wollen wir doch dem Benutzer unsere Dateistruktur nicht transparent machen. Im Querystring wird lediglich eine Ziffer übergeben, mit deren Hilfe wir im Programm dann den entsprechenden Dateinamen aus dem Array holen. Falls die Variable einen Wert hat, der nicht erlaubt ist, wird in diesem Beispiel die Homepage geladen.
Mit Bild
$arrDateien = array(1 => "inhalt_A.inc", 2 => "inhalt_B.inc"); if (array_key_exists($_GET["datei"], $arrDateien)) { include($arrDateien[$_GET["datei"]]); } else { include("home.inc"); }
Mit Bild
Die Funktion array_key_exists() prüft, ob der genannte Schlüssel im Array enthalten ist. Ist er vorhanden, wird die Variable $_GET["datei"] als Schlüssel für das Array $arrDateien verwendet und die entsprechende Datei wird eingebunden. Gibt es den Schlüssel nicht, wird die Homepage geladen.
Seien Sie übrigens zurückhaltend mit Fehlermeldungen in der Art "Einbruchversuch abgewehrt". Manche Programmierer machen das gern, um zu zeigen, dass sie schlauer sind als die Hacker. Allerdings kann es auch einem ganz unbedarften Benutzer passieren, dass er in diesem konkreten Beispiel eine falschen Querystring erzeugt, indem er zum Beispiel den Link auf diese Seite kopiert, und beim Kopieren ein Teil des Links verloren hat. Dieser Benutzer wäre zu Recht verärgert, wenn Sie ihm einen Manipulationsversuch unterstellten. Es reicht völlig, einen Fehler zu unterbinden.
In diesem Fall wird die im Tipp oben vorgestellte Kurzschreibweise für if-else-Anweisungen übrigens leider nicht funktionieren. Bei include() müssen Sie die klassische Schreibweise verwenden und auch die geschweiften Klammern auf jeden Fall setzen.
Diese Beispiele sollten Ihnen einige Anwendungsmöglichkeiten von Querystrings gezeigt, die Probleme bei der Übernahme von externen Variablen in unsere Programme aufgezeigt und einige kleine Kniffe vermittelt haben, wie die Überprüfung von Variablen erfolgen kann.