htmlspecialchars richtig nutzen – Fallstricke

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 &#039; 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.


Deprecated: Directive 'allow_url_include' is deprecated in Unknown on line 0