In der PHP-Welt scheint der Mythos vorzuherrschen, dass die Nutzung von htmlspecialchars sämtlichen Input in sämtlichen Situation sicher macht. Quasi die „one fits all“ – Funktion. Beginnen wir gleich mal mit einem Beispiel. Kein Witz, so gesehen:
function get_user_id($db) { $username = htmlspecialchars(trim($_POST['username'])); $res = $db->query("SELECT `id` FROM `accounts` WHERE `login` = '$username' LIMIT 1"); return $res[0]['id']; }
Cool! Schauen wir uns doch mal die Signatur von htmlspecialchars an:
string htmlspecialchars ( string $string [, int $flags = ENT_COMPAT | ENT_HTML401 [, string $encoding = 'UTF-8' [, bool $double_encode = true ]]] )
Das verflixte Hochkomma
Besonderes Augenmerk sei auf die Flags gelegt. ENT_COMPAT heißt „Konvertiert nur doppelte Anführungszeichen und lässt einfache Anführungszeichen unverändert.„. Also: Perfektes Einfallstor für SQL-Injection.
Gleiches ergibt sich im Formularhandling:
<?php $_GET['firstname'] = "'onmouseover='alert(/xss!/)'"; $firstname = htmlspecialchars($_GET['firstname'], ENT_COMPAT); ?> <input name='firstname' value='<?php echo $firstname ?>'>
Analog zu diesem PHP WTF-Artikel erfreut auch hier die !sinnvolle Wahl der Defaultparameter. Der Flags-Parameter sollte also in jedem Fall ENT_QUOTES enthalten, so werden nämlich auch Single-Quotes durch das entsprechende Pendant ' ersetzt.
Encoding
Seit PHP 5.4 ist der dritte Parameter string $encoding sinnvollerweise auf UTF-8 gesetzt. Vorher kam htmlspecialchars mit ISO-8859-1 als Standard daher. Bei der Frage des Encodings gilt: Immer dasselbe Encoding verwenden, das auch die Applikation selbst verwendet. Und das sollte ja in der Regel UTF-8 sein. Deswegen in Libraries am Besten eine eigene Wrapperfunktion bauen, die genau so aussieht:
function real_htmlspecialchars($string) { return htmlspecialchars($string, ENT_QUOTES, "UTF-8"); }
Und trotz dessen: Für Datenbankparameter ist htmlspecialchars tabu! PDO bietet die quote-Funktion und wer wirklich noch mit nacktem MySQL rumjuckeln muss freut sich über mysql_real_escape_string oder alterantiv die Escape-Funktion des verwendeten Frameworks.
Interessant auch, wie es „die Großen“ betreiben: Die u.a. von Symfony verwendete Template-Engine Twig (siehe twig_escape_filter) und der Zend-Escaper kann man sich mal zu Gemüte führen. Sehr lesenswert ist auch das eingereichte Proposal für einen vernünftigen, kontextabhängigen PHP-builtin-Escaper.