Bei PHP-Skripten konzentrieren sich die Sicherheitsaspekte im wesentlichen auf zwei Punkte. Das Verhindern von Cross-Site-Scripting (CSS oder auch XSS) und das Verhindern des Ausspähens von Daten bzw. Einschleusen von Code. Dazu ist es insbesondere notwendig, sämtliche Eingaben, die von außen kommen, zu überprüfen. Dies betrifft in der URL übergebene Daten, Formulardaten, Cookies und hochgeladene Dateien.

Bitte beachten Sie: Die Liste der hier genannten Sicherheitsvorkehrungen ist nicht als abgeschlossenes Sicherheitskonzept zu betrachten, sondern dient nur dem Aufspüren von grundlegenden Risiken.

Ein Sicherheitskonzept erfasst eine Software immer in Ihrer Gesamtheit und erstreckt sich konsequent über alle Bereiche der Datenverarbeitung. Das Absichern von Skripten stellt dabei nur einen Baustein im Gesamtkonzept dar.

Code einschleusen durch include()

Eine häufige Sicherheitslücke besteht darin, dass die Funktion include() mit variablen Parametern verwendet wird. In diesem Fall kann ein Angreifer fremden Code einschleusen, indem er eine URL als Parameter übergibt (z.B. http://evil-site.tld/exploit.txt). Dieser vom Angreifer frei bestimmbare Code wird dann so ausgeführt, als wäre er Bestandteil des Skriptes. Der Angreifer hat dann alle Möglichkeiten des Skripts zur Verfügung. Vermeiden Sie deshalb das Aufrufen von include() mit einem variablen Wert und verwenden Sie statt Variablen lieber Konstanten. Wenn die Verwendung von Variablen unvermeidbar ist, filtern Sie deren Inhalt entsprechend. Dies kann beispielsweise mit folgendem Code geschehen.

if (strpos($variable, '://') !== FALSE || strpos($variable, '../') !== FALSE)
die('Illegal string'); 

Dieser Code überprüft, ob die Zeichenketten ://, wie in http:// oder ftp:// oder .., wie in ../../secret/passwords im String $variable enthalten sind, und bricht das Ausführen ggf. ab.

SQL-Injection

Wenn ein Skript Datenbankabfragen benutzt, kann ein Angreifer bei falscher Programmierung des Skripts relativ leicht beliebigen SQL-Code einschleusen. Dadurch kann er z.B. Daten, auf die er sonst keinen Zugriff hat, lesen, ändern oder sogar löschen.

Angenommen Sie verwenden in einem Skript folgenden Code:

[...]
$sql = "SELECT * FROM adressen WHERE name='".$_GET['name']."'";
$result = mysql_query($sql);
[...]        

Diese bzw. eine ähnliche Abfrage kann auftauchen, wenn Sie aus einer Tabelle, welche Adressen enthält, die Adresse zu einem bestimmten Namen herausfiltern möchten.
Der Angreifer kann nun die URL http://domain.tld/skript.php?name=';DELETE FROM adressen WHERE 1=1 OR name=' aufrufen.
Dies ergibt dann im Skript die SQL-Abfrage SELECT * FROM adressen WHERE name=''; DELETE FROM adressen WHERE 1=1 OR name='';. Zunächst wird der Datenbankserver also alle Datensätze auswählen, bei denen das Feld Name einen leeren String enthält, und anschließend werden alle Datensätze aus der Tabelle gelöscht. Natürlich lässt sich eine solche Lücke auch für andere Zwecke ausnutzen, das Löschen von Tabelleninhalten soll hier nur als Beispiel dienen.

Um eine solche Sicherheitslücke zu vermeiden, sollten Variablen in SQL-Abfragen immer durch die Funktion mysql_real_escape_string() transformiert werden. Dabei werden Sonderzeichen wie das einfache Anführungszeichen so maskiert, dass sie als Bestandteil des gesamten Strings und nicht als Zeichen für das Ende des Strings interpretiert werden. Wenn Werte außerhalb von Anführungszeichen übergeben werden (z.B. numerische Werte), verwenden Sie möglichst die Funktion intval(). So wird sichergestellt, dass auch tatsächlich nur ein numerischer Wert verwendet wird, hier ein Beispiel dazu:

Übergabe von Parametern in URLs

Die Übergabe von Parametern in URLs (z.B. http://domain.tld/script.php?id=1) ist ein weitverbreitetes Mittel, um Argumente an Skripte zu übergeben. Man sollte sich dabei jedoch immer bewusst sein, dass diese Argumente beliebig vom Benutzer gesetzt werden können. Das bedeutet, dass ihr Inhalt als nicht vertrauenswürdig betrachtet werden muss. Das gleiche gilt übrigens für Daten, die per HTTP-Post übermittelt werden, sowie für Cookies.

Wichtig ist dies, wenn man von einem Skript auf ein anderes weiterleitet und dabei in der URL oder als Cookie Parameter übergibt. Das neue Skript kann eben diese Parameter nun nicht als vertrauenswürdig erachten, sondern muss alle neu prüfen. Oft ist es einfacher, wenn die Session-Funktionen von PHP benutzt werden. Daten, die einmal geprüft und in der Session gespeichert wurden, können fortan als sicher eingestuft und benutzt werden. Der Benutzer hat keine Möglichkeit, den Inhalt der Session direkt zu ändern.
Der Session-Identifier wird wiederum als Parameter mit der URL übergeben. Da es sich dabei um einen zufällig erzeugten String handelt, ist die Wahrscheinlichkeit, dass ein Angreifer den Identifier einer fremden Session erraten kann, äußerst gering.

Globale Variablen

In älteren PHP-Versionen wurden Parameter, die von Formularen oder in der URL übergeben wurden, in globale Variablen eingetragen. Aus Kompatibilitätsgründen bilden aber auf den meisten Systemen auch aktuelle Versionen dieses Verhalten noch immer nach.
Wenn z.B. die URL http://domain.tld/skript.php?variable=inhalt aufgerufen wird, wird im Skript ein Variable mit dem Namen $variable und dem Inhalt inhalt gesetzt.

Zum Problem kann dies werden, wenn man globale Variablen intern verwendet, ohne sie vorher richtig zu initialisieren.

Beispiel:

<?php
if ($_GET['password'] == 'geheim') {
$admin = true;
}

[...]

if ($admin) {
// Aktionen, die einem Administrator, der das Passwort kennt, vorbehalten sind
[...]
}
?>

Ein Angreifer kann einfach die URL http://domain.tld/skript.php?admin=1 aufrufen und hat sofort Administrator-Rechte, weil die Variable $admin damit true zurückliefert.

Mit korrekter Initialisierung kann dies verhindert werden

<?php
$admin = false;

if ($_GET['password'] == 'geheim') {
$admin = true;
}

[...]

if ($admin) {
// Aktionen, die einem Administrator, der das Passwort kennt, vorbehalten sind
[...]
}
?>

Nun wird der zuvor für $admin gesetzte Wert zunächst überschrieben und nur auf true gesetzt, wenn auch tatsächlich das Passwort übergeben wurde.

In anderen Szenarien kann das Weglassen der Initialisierung z.B. auch für SQL-Injections ausgenutzt werden. Aus diesem Grund sollte man sich angewöhnen, grundsätzlich alle Variablen zu initialisieren, obwohl dies die Syntax von PHP nicht verlangt.

Cross-Site-Scripting

Unter Cross-Site-Scripting versteht man den Fall, dass ein Angreifer JavaScript-Code in eine fremde Seite einschleust, um Informationen zu erhalten, auf die er normal keinen Zugriff hat. Dies kann ausgenutzt werden, um an fremde Session-IDs zu gelangen oder um Benutzernamen auszuspähen.
Dazu erzeugt ein Angreifer eine URL, in der er Parameter setzt, die im Text der Seite so unmittelbar wiedergegeben werden. Wenn dieser Text nun Skript-Code enthält, wird dieser im Sicherheitskontext der Seite ausgeführt, kann also z.B. Cookies, die von dieser Seite gesetzt wurden, auslesen. Die Gefahr an dieser Art Angriff ist, dass er für den Benutzer nahezu nicht zu bemerken ist wenn er in einem versteckten Frame erfolgt.

Die Lösung liegt darin, im Skript über sämtliche auszugebende Variablen (insbesondere solche, die Benutzereingaben enthalten), die Funktion htmlentities() anzuwenden.
Diese ersetzt Zeichen mit Sonderbedeutung in HTML,wie <, >, >', ", durch die entsprechenden Entitäten. Durch diese einfache Maßnahme können Sie ein Großteil der Angriffsmöglichkeiten für Cross-Site-Scripting ausschließen.