Changelog:
13 März 2011 - Bei HTML-Ausgabe von GET oder POST-Variablen wird _immer_ htmlspecialchars() benötigt!
21 September 2010 - noch ein $wb->add_slashes() in $wb->strip_slashes() geändert.
25 August 2010 - viele $wb->add_slashes() zu $wb->strip_slashes() geändert -- macht natürlich mehr Sinn.
19 April 2010 - Beispiel zu strip-tags() erweitert
14 April 2010 - $wb->add_slashes() zu den meisten Beispielen hinzu.
07 April 2010 - vergessenen ENT_QUOTES bei Beispiel zu htmlspecialchars() eingefügt.
05 April 2010 - Kommentar in Beispiel preg_quote erweitert.
05 April 2010 - fertiggestellt
Hallo,
dritter Teil:
Behandlung von GET/POST-Daten.Die Benutzung von GET- oder POST-Daten völlig ohne Sicherungsmaßnahmen ist das Haupt-Einfallstor für eine ganze Reihe von Gefahren, allen voran XSS und SQL-Injection. Darum ist bei der Arbeit mit Werten aus dem GET- oder POST-Array immer Vorsicht notwendig.
Einschub: $_REQUEST sollte auf keinen Fall benutzt werden, da bei Benutzung von $_REQUEST - je nach Server-Konfiguration - die Gefahr besteht, daß ein Angreifer einen Wert aus $_POST durch einem untergeschobenen Wert aus $_GET "überschreibt", oder anders herum. Beispiele für Gefahren, die sich aus der "unkontrollierten" Benutzung von $_GET oder $_POST ergeben:
$id = $_GET['user_id'];
$query_user = $database->query("SELECT permissions FROM table WHERE user_id='$id'");
Wenn auf dem Server magic_quotes_gpc deaktiviert ist entsteht mit
http://...php?user_id=-10' OR name='Admin'; --
dies
SELECT permissions FROM table WHERE user_id='-10' OR name='Admin'; --'
Mit aktiviertem magic_quotes_gpc funktioniert dieser Hack nicht mehr
SELECT permissions FROM table WHERE user_id='-10\' OR name=\'Admin\'; --'
(Einschub: Dieses Beispiel ist natürlich blöd! Wer sowas im Frontend benutzt dem gehört der Computer entzogen!)Aber selbst magic_quotes_gpc schützt nicht vor XSS-Angriffen:
$class = $_GET['class'];
echo "<p class=\"".$class."\">".$text."</p>";
Mit aktiviertem magic_quotes_gpc
http://....php?class="><script>alert(String.fromCharCode(39)%2BString.fromCharCode(33)%2BString.fromCharCode(39));</script>
ensteht
<p class="\"><script>alert(String.fromCharCode(39)+String.fromCharCode(33)+String.fromCharCode(39));</script>">TEXT</p>
Funktionen für die Benutzung mit mySQL-QueriesDie folgenden Methoden stehen sowohl in der Klasse wb als auch in der Klasse admin bereit.
Im Backend werden sie über
$admin-> angesprochen, im Frontend über
$wb->$wb->add_slashes($string) - Maskiert bestimmte Zeichen aus $string, d.h. stellt ihnen ein
\ voran, wenn auf dem Server magic_quotes_gpc
deaktiviert ist. Wenn magic_quotes_gpc aktiviert ist erledigt PHP dies selber.
Die folgenden Zeichen werden maskiert: ' " \ \x00 (Null-Byte)
Diese Funktion dient dazu, für alle Strings aus $_GET oder $_POST, unabhängig von der Server-Konfiguration, immer korrekt maskierten Strings zu erhalten.
Wie oben gesehen, ist diese Maskierung nur für Datenbank-Operationen wichtig. Für HTML-Ausgabe siehe htmlspecialchars()/strip_slashes().Eingabe:
http://...php?name=o'Reilly<?php
$name = $_GET['name']; // möglicherweise "o'Reilly" oder "o\'Reilly"
$name = addslashes($_GET['name']); // FEHLER: liefert "o\'Reilly" oder "o\\\'Reilly"
$name = $wb->add_slashes($_GET['name']); // RICHTIG: auf jeden Fall "o\'Reilly"
$wb->strip_slashes($string) - Entfernt (führende)
\ aus
$string,
wenn auf dem Server magic_quotes_gpc aktiviert ist.
Diese Funktion dient dazu, für alle Strings aus $_GET oder $_POST, unabhängig von der Server-Konfiguration, immer den Originalstring zu erhalten.
Eingabe:
http://...php?name=o'Reilly \text<?php
$name = $_GET['name']; // möglicherweise "o'Reilly \text" oder "o\'Reilly \\text"
$name = stripslashes($_GET['name']); // FEHLER: liefert "o'Reilly text" oder "o'Reilly \text"
$name = $wb->stip-slashes($_GET['name']); // RICHTIG: auf jeden Fall "o'Reilly \text"
$wb->get_post_escaped($field) - enspricht
$wb->add_slashes($_POST[$field])Weitere wichtige Funktionen von PHP:addslashes($string) - Maskiert $string.
addslashes() wird gerne benutzt, um Werte die in einer Datenbank-Abfrage benutzt werden sollen zu maskieren:
<?php
$name = $wb->strip_slashes($_GET['name']); // Original-String herstellen
echo "Der Name ist: ".htmlspecialchars($name, ENT_QUOTES);
$name = addslashes($name); // maskieren
$query = $database->query("SELECT * FROM table WHERE name='$name'");
Allerdings ist die Benutzung von addslashes() zu diesem Zweck seit bekanntwerden eines Exploits in Verruf gekommen.
Zwar muß, damit dieser Exploit funktioniert, mySQLs default-charset auf GBK aka simplified Chinese stehen, aber nichtsdestotrotz sollte man auf mysql_real_escape_s tring() umsteigen.mysql_real_escape_s tring($string) - maskiert bestimmte Zeichen in $string für die Verwendung in einem mySQL-Query. Hierfür muß eine geöffnete Verbindung zu mySQL bestehen.
Diese Funktion wird als sicherer als addslashes() angesehen, da sie zum einen mehr Zeichen maskiert (\x00, \n, \r, \, ', " und \x1a), und sie zum anderen nicht wie addslashes() anfällig für Multi-Byte-Character Exploits ist.
Die Funktion wird genauso wie addslashes() benutzt:
<?php
$name = $wb->strip_slashes($_GET['name']); // Original-String herstellen
echo "Der Name ist: ".htmlspecialchars($name, ENT_QUOTES);
$name = mysql_real_escape_string($name); // maskieren
$query = $database->query("SELECT * FROM table WHERE name='$name'");
is_numeric($string), intval($string), casting, ...Eine ganze Reihe von Funktionen, mit deren Hilfe man Werte auf ihren Daten-Typ hin untersuchen, oder sie in einen bestimmten Daten-Typ umwandeln kann.
<?php
// $user_id must be integer:
if(is_numeric($_GET['user_id'])) {
$user_id = $_GET['user_id'];
} else {
$user_id = 0;
}
$query = $database->query("SELECT * FROM table WHERE id=$user_id");
Kürzer geht es so:
<?php
// $user_id must be integer:
$user_id = intval($_GET['user_id']);
$query = $database->query("SELECT * FROM table WHERE id=$user_id");
| Funktion | Resultat |
| is_numeric("10") | TRUE |
| is_numeric(" 10 ") | FALSE |
| is_numeric("10 text") | FALSE |
| is_numeric("text 10") | FALSE |
| is_numeric("") | FALSE |
| intval("10") | 10 |
| intval(" 10 ") | 10 |
| intval("10 text") | 10 |
| intval("text 10") | 0 |
| intval("") | 0 |
Hier erkennt man auch warum man niemals den Wert 0 für IDs in der Datenbank benutzen darf.
addcslashes($string, $charlist) - Maskiert alle Zeichen aus $charlist in $string.
Eine kaum benutzte Funktion, die jedoch für eine bestimmte Sache sehr nützlich ist, und zwar für SQL-Queries, die
LIKE benutzen.
Das folgende Query soll eine Liste alle Personen erzeugen, deren Name mit
$name beginnt<?php
$name = mysql_real_escape_string($wb->strip_slashes($_GET['name']));
$query = $database->query("SELECT * FROM table WHERE name LIKE '".$name."%');
Wenn auf dem Feld
name ein Index liegt, ist dieses Query ausreichend performant.
Ein Angreifer könnte nun einen DOS-Angriff (Denial of Service) starten, indem er eine große Zahl an Suchanfragen in der Form
http://...php?name=%_a an den Server schickt. Denn dadurch entsteht
SELECT * FROM table WHERE name LIKE '%_a%'
(Suche alle Namen, die beliebig beginnen, mit einem beliebigen Zeichen weitergehen, gefolgt von einem a, und dann beliebig enden), was in einer furchtbaren Table-Scan-Orgie endet.
Gegenmaßnahme:
<?php
$name = mysql_real_escape_string($wb->strip_slashes($_GET['name']));
$name = addcslashes($name, '_%'); // maskiere % und _
$query = $database->query("SELECT * FROM table WHERE name LIKE '".$name."%');
Nun entsteht
SELECT * FROM table WHERE name LIKE '\%\_a%'
(Suche alle Namen die mit % beginnen, gefolgt von einem _, gefolgt von a, und die beliebig enden), und der Angriff ist abgewehrt.
Funktionen für die Benutzung mit HTML-Ausgabenhtmlspecialchars($string, ENT_QUOTES) - Wandelt alle & < > ' " in HTML-Entities (&, <, ...) um.
<?php
$name = $wb->strip_slashes($_GET['name']);
$name = htmlspecialchars($name, ENT_QUOTES);
echo "Name: <strong>$name</strong>";
Eingabe:
Hans<script>alert('Hallo!');</script>Ausgabe:
Hans<script>alert('Hallo!');</script>Nicht schön, aber sicher.
strip_tags($string, $allowed_tags) - entfernt alle HTML-Tags aus $string. Die in $allowed_tags angegebenen Tags werden nicht entfernt.
Achtung: Die alleinige Nutzung von strip_tags() reicht nicht aus, da dabei " und ' erhalten bleiben, wodurch XSS-Angriffe möglich werden!
<?php
$name = $wb->strip_slashes($_GET['name']);
$name = htmlspecialchars(strip_tags($name), ENT_QUOTES);
echo "Name: <strong>$name</strong>";
Eingabe:
Hans<script>alert('Hallo!');</script>Ausgabe:
Hansalert('Hallo!');Auch nicht schön, aber wiederum sicher.
Im nächsten Beispiel sieht man, warum htmlspecialchars() immer notwendig ist.
Außerdem zeigt es, warum von der Benutzung von
$allowed_tags eher abzuraten ist:
<?php
$name = $wb->strip_slashes($_GET['name']);
$name = strip_tags($name, "<b><strong><i>");
echo "Name: <strong>$name</strong>";
Eingabe:
Hans <b onmouseover="alert('Hello!')">Hänschen</b> Mustermann<script>alert('Hallo!');</script>Ausgabe:
Hans <b onmouseover="alert('Hello!')">Hänschen</b> Mustermannalert('Hallo!');Insgesamt kann man sagen, daß man auf strip_tags() eher verzichten sollte, da es z.B. auch einfach alles zwischen
< und dem nächsten
> (oder bis zum Stringende) entfernt:
<?php
echo strip_tags($wb->strip_slashes($_GET['name']);
Eingabe:
Außerdem gilt immer noch 1<2. Wäre sonst ja auch schlimm!Ausgabe:
Außerdem gilt immer noch 1
Funktionen für die Benutzung in Linksurlencode($string) - Kodiert
$string zur Benutzung in einer URL.
Bestimmte Zeichen (z.B. : / ? &) müssen in einer URL kodiert werden, damit sie nicht als "Steuerzeichen" interpretiert werden. Dazu dient diese Funktion.
Beispiel: Der String "R&B" soll als Suchbegriff per URL übergeben werden:
http://www.example.org/wb/search/index.php?search=R&B
funktioniert nicht, da das
& als "Steuerzeichen" interpretiert wird (& kennzeichnet den Anfang eines neuen Wertes). Statt
search="R&B" erhält man
search="R" und
B="".
Richtig geht es so:
<?php
$search = urlencode("R&B"); // erzeugt "R%26B"
echo "http://www.example.org/wb/search/index.php?search=".$search;
Ausgabe:
http://www.example.org/wb/search/index.php?search=R%26B
Beachten Sie, daß in der Zielseite dieser Wert
automatisch dekodiert wird! Ein Aufruf von urldecode() ist
nicht notwendig.
<?php
$search = $wb->strip_slashes($_GET('search'));
echo $search; // Ausgabe: R&B
Will man mehrere Werte übergeben, trennt man die einzelnen Werte durch
&.
& alleine würde auch reichen, allerdings besteht die Gefahr, daß dadurch HTML-Entities entstehen:
<?php
echo '<a href="http://www.example.org/pages/page.php?row=4©=1">link</a>';
Erzeugt:
http://www.example.org/page/page.php?row=4©=1
Besteht ein Wert mit Sicherheit nur aus einer Zahl kann auf
urldecode() verzichtet werden.
Komplettes Beispiel:
<?php
$link = WB_URL.'/modules/mod/file.php';
$url_query = '?';
$url_query .= 'page_id='.$page_id;
$url_query .= '&';
$url_query .= 'name='.urlencode($name);
echo '<a href="'.$link.$url_query.'">link</a>';
oder kompakter
<?php
echo '<a href="'.WB_URL.'/modules/mod/file.php?page_id='.$page_id.'&name='.urlencode($name).'">link</a>';
Sonstige Funktionenpreg_quote($string, $begrenzer) - maskiert Spezielle-Zeichen in $string zur Verwendung in einem Regulären-Ausdruck. $begrenzer ist das als Begrenzer benutzte Zeichen, das ebenfalls maskiert wird.
Für den Fall, daß eine Benutzer-Eingabe, oder allgemein ein unbekannter String, in einem Regulären-Ausdruck benutzt werden soll, müßen bestimmte Zeichen (z.B. * und ?) maskiert werden.
<?php
$str = $wb->strip_slashes($_POST['suchwort']);
$str = preg_quote($str, '~'); // maskiert alle speziellen Zeichen, und ~, das in der nächsten Zeile als Begrenzer benutzt wird
if(preg_match('~\b'.$str.'~i', $text)) {
// Treffer
}
Anderenfalls kann ein Fehler auftreten (z.B.
suchwort="V8("), oder es könnte sogar für einen DOS-Angriff genutzt werden.
Siehe auch:
Filter-Erweiterung die seit PHP 5.2 benutzt werden kann.
thorn.