Gelegentlich habe ich das Vergnügen, in fremdem PHP-Code zu wühlen. Über die Nichteinhaltung irgendwelcher Coding-Standards ärgere ich mich deshalb nicht mehr, weil das der Normalfall ist. Was mich aber immer wieder bass erstaunt, ist die Anfälligkeit für SQL-Injections. Im Jahre 2010 sollte sich doch wirklich auch zum letzten PHP-Frickler herumgesprochen haben, das Konstrukte wie diese beiden hier wahnsinnig sind:
$sql = "SELECT * FROM tabelle WHERE ort = '" . $_POST['ort'] . "'";
$sql = "SELECT * FROM users WHERE username = 'admin'
AND password = '" . $_POST['password'] . "'";
Gibt man nun im zweiten Beispiel ein kleines leckmich' OR 1 OR password = ' ein, macht PHP daraus folgendes SQL-Statement:
$sql = "SELECT * FROM users WHERE username = 'admin'
AND password = 'leckmich' OR 1 OR password = ''";
Et voilà, wir sind als Admin eingeloggt. Das ist ein echtes Beispiel, das mir mal den Zugang zu einem Admin-Backend ermöglicht hat, zu dem ich auf die Schnelle keine Zugangsdaten bekommen konnte. In dem Fall war das ein Segen, weil es mir die Arbeit erleichtert hat, aber auf die Idee hätte auch irgendwann jemand anderes kommen können. Es gibt auch ein legendäres XKCD dazu, das ich gerne als Einstieg in meiner Vorlesung benutze, wenn es um Computersicherheit geht.
Manchmal wird $_POST immerhin vorher irgendwie geprüft oder gefiltert, Konstrukte wie das folgende sind also im Grunde recht klug gedacht:
$connection = mysql_connect('localhost', 'user', 'password');
mysql_select_db("dbnamme", $connection);
if ( preg_match('/[\w\(\)\.-]/iu', $_POST['ort']) )
{
if ( $result = mysql_query("SELECT * FROM tabelle WHERE ort = '" . $_POST['ort'] . "'") )
{
$row = mysql_fetch_array($result);
}
}
Allerdings neigt man bei solchen Sachen dazu, im Eifer des Gefechts die wichtige Prüfung auf ungültige Zeichen zu vergessen oder unter bestimmten Randbedingungen gar nicht ausführen zu lassen. Es gibt unzählige Möglichkeiten, eine SQL-Injection zu vermeiden, irgendeine davon sollte genutzt werden. Mein Favorit ist eindeutig die Nutzung eines Frameworks, das sich darum kümmert, oder eben die eigene Datenbankprogrammierung ausschließlich über prepared statements und PDO. Letzteres sieht dann für obige Funktionalität vereinfacht etwa so aus:
$dbh = new PDO('mysql:host=localhost;dbname=dbname', 'user', 'password');
$stmt = $dbh->prepare("SELECT * FROM tabelle WHERE ort = :ort");
if ( $stmt->execute(array(':ort' => $_POST['ort'])) )
{
$row = $stmt->fetch(PDO::FETCH_ASSOC);
}
Hier sorgt das prepared statement von PDO dafür, dass zuerst der SQL-String mit Platzhalter an die Datenbank übertragen wird, und später getrennt davon der Inhalt von $_POST['ort']. Keine SQL-Injection möglich an dieser Stelle, wir können ruhig schlafen. Und das beste ist, es ist nicht mal wirklich komplizierter, schwieriger oder mit mehr Code verbunden, einfach sicherer. PDO kann übrigens noch mehr, etwa sehr komfortabel mit Transaktionen umgehen, die dann wichtig werden, wenn man mehrere voneinander abhängige Schreiboperationen auf der Datenbank durchführt und die nur entweder ganz oder gar nicht haben will. EIn Blick die Dokumentation zu PDO zeigt noch etliche andere Vorteile auf und PHP mindestens in Version 5.1 sollten wir inzwischen doch hoffentlich alle haben.
Neben SQL-Injections gibt es noch unzählige weitere Angriffsflächen auf Webapplikationen, aber wenn man schon an so offensichtlichen Grundlagen scheitert, sollte man die eigene Programmierleistung noch mal grundlegend auf den Prüfstand stellen. Vielleicht sollte man auch lieber stricken oder kochen, wenn man da grundlegende Fehler macht, fängt man sich nicht direkt einen Servereinbruch ein. Wer fürs Web programmiert, trägt eine gewisse Verantwortung; sich also zumindest ein klitzekleines Basiswissen an sicherer Programmierung zuzulegen, darf doch erwartet werden. Vor allem, wenn in dem Admin-Interface Kundendaten einsehbar oder gar veränderbar sind, oder man dort anderen Schindluder mit wichtigen Dingen treiben kann. War in obigem Fall nicht so, immerhin.
Klar passieren einem Programmierfehler, aber Daten ungeprüft aus $_POST (oder sonstigen vom Benutzer kontrollierten Quellen) in eine Datenbankabfrage zu übernehmen geht wirklich ganz und gar nicht, nie und nimmer. Das ist ein unverzeihlicher Fehler und zeugt von mangelndem Grundverständnis der Thematik. Wenn ich sowas sehe, frage ich mich immer, ob der jeweilige Programmierer überhaupt ansatzweise weiß, was er da macht. Wenn das ein Anfänger bei seiner ersten Clan-Homepage macht, kann man darüber ja noch lachen, aber wenn Geld fließt, kann man sich solche Sperenzchen einfach nicht mehr leisten.
Nachtrag 01.11.2010: Besonders schön sind übrigens Kommentare wie //Schutz vor SQL-Injection
, die nur wenige Zeilen unterhalb einer anderen gefährdeten SQL-Abfrage kommen. Offenbar ist das Problem grundsätzlich bekannt, aber etwas dagegen tun tut man dann doch nur, wenn man mal Lust darauf hat. Warum nur?
Die Kommentarfunktion wurde vom Besitzer dieses Blogs in diesem Eintrag deaktiviert.
Noch keine Kommentare