Bei der Whitelist-Überprüfung handelt es sich um eine Prüfung auf gültige Daten. Das heißt, nur gültige Daten in Variablen werden an ein Subsystem, wie eine Datenbank weitergegeben. Bei Blacklist-Überprüfungen werden hingegen alle bekannten "bösen" Zeichen aus dem String entfernt und so versucht, die Eingabe gültig zu machen. Da Blacklists von einer meist unzutreffenden Annahme ausgehen - nämlich der, daß alle bösen Zeichen oder Zeichensequenzen dem Entwickler bekannt sind - sollte so weit wie möglich auf Blacklisting Überprüfungen verzichtet werden. Stattdessen sollte stets auf Whitelists zurückgegriffen werden.
Wie Whitelists funktionieren, soll an einem Beispiel dargestellt werden. Ein Online Shop hat für Kleider eine bestimmte Reihe von Größen in seinem Produktangebot. Diese werden mittels einer Auswahlbox in einem Formular angeboten:
< form action="check.php" method="post">
<select name="groesse">
<option value="m">Größe M</option>
<option value="l">Größe L</option>
<option value="xl">Größe XL</option>
[..]
</select>
</form>
Die drei Optionen "m","l" und "xl" und wirklich nur diese Optionen sollen an ein SQL Statement weitergegeben werden. Angreifer könnten jedoch auf die Idee kommen, ein manipuliertes Formular an den Onlineshop zu versenden, das zusätzlich die Größe XXXL enthält - vor dem Einfügen der Bestellung in die Datenbank müssen solche ungültigen Angaben ausgefiltert und gelöscht werden.
Folgende Überprüfung hilft dabei:
<?php
$saubere_vars = array();
$richtige_groessen = array("m", "l", "xl");
if ( in_array($_POST['groesse'],$richtige_groessen))
$saubere_vars['groesse'] = $_POST['groesse'];
else
// Error
?>
Hier verwenden wir ein zusätzliches Array $saubere_vars, in dem alle Variablen (im Beispiel nur die Variable groesse' aus $_POST) nach der Überprüfung gespeichert werden. Es ist prinzipiell eine gute Idee, saubere und geprüfte Variablen ("tainted data") in einen extra Bereich zu schreiben. In diesem Beispiel kann der Variable $saubere_vars['groesse'] vertraut werden, denn sie wurde zuerst initialisiert und dann mit einem gültigen Wert aus dem $_POST Array befüllt.
Würde die Variable $saubere_vars am Skriptanfang nicht initialisiert und die Konfigurationsdirektive register_globals sei aktiviert, könnte über die URL ein $saubere_vars Array Übergeben werden und die Überprüfung wäre umsonst. Daher ist eine korrekte Initialisierung vor Benutzung erforderlich.
Zur Überprüfung freier Stringeingaben in Formularen, wie zum Beispiel eine E-Mail Adresse, bieten sich Regular Expressions an.
<?php
$saubere_vars = array();
$email_pattern = '/^[^@s]+@([-a-z0-9]+.)+[a-z]{2,}$/i';
if (!preg_match($email_pattern, $_POST['email']))
// Error
else
$saubere_vars['email'] = $_POST['email'];
?>
In diesem Beispiel wird nur eine gültige Mailadresse im Format user(at)domain.tld vom Regulären Ausdruck akzeptiert und in das Array der "gesäuberten" Daten einfügt.
Bevor das Script solche Variablen an Subsysteme wie Datenbanken weitergibt, sollten sie grundsätzlich maskiert werden - also alle Sonderzeichen, speziell Quotes durch \ zu "entschärfen". Das sollte stets erfolgen, wenn Daten den Kontext wechseln, also etwa in eine Datenbank eingefügt oder auf dem Dateisystem abgelegt werden. Speziell für das Quoting von Daten für MySQL gibt es die PHP-Funktionen mysql_escape_string() und mysql_real_escape_string(). Diese Funktionen fügen Escaping-Zeichen so in einen String ein, daß er in einem MySQL-Statement keine Fehler mehr verursachen kann.
Während mysql_escape_string() seit PHP 4.3.0 deprecated ist, also nicht mehr verwendet werden sollte, wird von der neueren Funktion mysql_real_escape_string() der momentane Zeichensatz der Datenbank beachtet, wenn ihr ein gültiger Resource Identifier übergeben wurde. %-Zeichen und Unterstriche werden grundsätzlich nicht maskiert.
Auch hier ist Vorsicht geboten: Falls die Konfigurationsdirektive "magic_quotes_gpc" aktiviert ist (php.ini oder .htaccess), so werden Anführungszeichen automatisch vor der Ausführung des eigentlichen Scripts markiert. Würde man auf diesen bereits maskierten String nochmals mysql_real_escape_string() anwenden, wären alle Quotes doppelt markiert.
Um derlei Doppelmaskierungen zu vermeiden, sollte vor der Verwendung von mysql_real_escape_string() auf bereits vorhandenes Quoting überprüft werden.
<?php
$sauberevars = array();
if (ini_get(magic_quotes_gpc')) {
$_POST['query'] = stripslashes($_POST['query'];
}
$sauberevars['query'] = mysql_real_escape_string($_POST['query']);
?>
Eine einfachere, jedoch nicht portable Möglichkeit ist natürlich, magic_quotes_gpc per .htaccess oder php.ini auszuschalten.
Zusammenfassend kann man sagen, das man
1. nur gültige Daten nach Typ- und Inhalts-Prüfung an Subsysteme wie Filesystem oder Datenbank weitergeben sollte,
2. ein Namensschema für Variablen erarbeiten sollte, um geprüfte und vertrauenswürdige Variablen von ungeprüften zu unterscheiden,
3. Daten, die an eine MySQL-Datenbank weitergereicht werden sollen, zuvor mit mysql_real_escape_string() maskieren sollte.