Articlename: SQL-Injection Keywords: SQL-Injection, mysql, php Date: 31.03.2008, 20:07 Views: 20202 Categoryname: Security ---------------------------------------- Einführung ------------------ In letzter Zeit häufen sich die Meldungen über SQL-Injection Lücken in bekannte OpenSource Projekten. Viele Entwickler sind sich überhaupt nicht bewusst, dass die Strings, die sie dem SQL Query übergeben nicht unbedingt sicher sein müssen. Dieses Tutorial behandelt SQL-Injection in MySQL Datenbanken. In MySQL-Queries werden oft irgendwelche Variablen (meistens Integer) übergeben, die entweder per URL, oder POST-Formular, oder GET-Formular übergeben werden. Diese Variablen können, wenn sie nicht ordentlich überprüft werden den query manipulieren. Natürlich braucht man Kenntnisse über die Datenbank, damit man einen erfolgreichen SQL-Injection Angriff überhaupt durchführen kann, aber "security by obscurity" ist nicht die Technik die ich bevorzuge. Angriffsmöglichkeiten von SQL-Injections ------------------ Hier sind einige Beispiele, was ein Angreifer durch SQL-Injection erreichen könnte: - Zugriff auf Daten, für die keine Berechtigung vorhanden ist Beispiel: Der Angreifer setzt die Spalte für die Rechte auf den entsprechenden Wert, und kommentiert den Rest aus. - Veränderung von Daten in der Datenbank Beispiel: Der Angreifer verändert eine INSERT/UPDATE Abfrage so, das sie andere Daten manipuliert. - Root Status Beispiel: Der Angreifer verändert das Passwort vom Benutzer root. - Einrichten einer Shell auf dem Datenbankserver Der Angreifer führt eine Datei aus, die dem Angreifer remote Zugriff auf das System gewährt. Das sind natürlich nur ein paar Beispiele, der Kreativität ist praktisch keine Grenze gesetzt... Szenarien ------------------ Hier sind einige Beispiele, die die unzureichende Überprüfung der Input-Variablen erläutern, und dem Angreifer Vorteile bringen: - Beschreibung: Der MySQL-Query gibt den Text eines Artikels aus, wenn der Benutzer die entsprechenden Rechte hat. Bugs: Das Script überprüft die Variablen überhaupt nicht, und macht keine Anführungszeichen in den Abfragen. ------- Code ------- $test = $_GET['test']; //Der Benutzer ist in diesem Fall 2 $UserID = 2; $Query = "SELECT artikel_text FROM artikel LEFT OUTER JOIN user ON(user_id = '$UserID') WHERE artikel_id = $test AND user_group = 3"; echo('Mysql Query: ').$Query.' '; $test = mysql_fetch_array($Query); echo($test['artikel_text']); ------- End-Code ------- Normalerweise wird die Seite mit http://url.xyz/index.php?test=1 aufgerufen, und ein Benutzer, der nicht in der Benutzergruppe '3' ist bekommt keinen Text geliefert. Bei http://url.xyz/index.php?test=1 AND user_group = 1/* schaut die Sache schon anders aus, hier wird folgendes ausgegeben: ------- Note: query ------- SELECT artikel_text FROM artikel LEFT OUTER JOIN user ON(user_id = '$UserID') WHERE artikel_id = 1 AND user_group = 1/* AND user_group = 3" ------- End-Note ------- Das bewirkt, das alles nach dem /* Kommentar weg ist, und die user_group auf 1 gesetzt wird, der Artikel wird ausgegeben. Der Angriff war erfolgreich. - Beschreibung: MySQL gibt einen Text, und den Titel aus. Bugs: Das Script überprüft die Variablen überhaupt nicht, und macht keine Anführungszeichen in den Abfragen. ------- Code ------- $test = $_GET['test']; //Der Benutzer ist in diesem Fall 2 $UserID = 2; $Query = "SELECT artikel_text, artikle_title FROM artikel WHERE artikel_id = $test; echo('Mysql Query: ').$Query.' '; $test = mysql_fetch_array($Query); echo('Text: '.$test['artikel_text'].' Title:'.$test['artikel_title']); ------- End-Code ------- Wenn jetzt http://url.xyz/index.php?test=1 aufgerufen wird, wird der Artikel ganz normal ausgegeben, http://url.xyz/index.php?test=1 UNION SELECT user_password, user_username FROM user WHERE user_id = 1/* gibt uns aber das Passwort, und den usernamen aus. Gegenmaßnahmen ------------------ Die wichtigste Verteidigung gegen Mysql-Injections ist das überprüfen der Eingangsvariable n. Des weiteren können Strings, die nicht escaped sind, wie im letzten Kapitel gezeigt, Probleme bereiten. Hier sind einige Maßnahmen gegen Mysql Injection aufgelistet: - Anführungsstriche verwenden Anführungsstriche werden dazu verwendet um Variablen in SQL-Queries zu benutzen. Beispiel: ------- Code ------- SELECT z FROM xyz WHERE id = '$Test' ------- End-Code ------- Ein Angreifer, der $Test verändert kann nicht aus den Anführungsstrichen ausbrechen, mit der Voraussetzung das $Test so geparst wurde, dass alle ' mit \' maskiert sind. Man sollte sich allerdings nicht zu stark darauf verlassen, diese Methode ist nur ein Teil von den Massnahmen die getroffen werden sollten. - Keine Strings in numerischen Variablen zulassen Die Funktion settype wandelt Variablen in einen Integer um: ------- Code ------- settype($Variable, 'integer'); ------- End-Code ------- Mit der Funktion is_numeric kann man überprüfen ob eine Variable Zeichen enthält: ------- Code ------- if(is_numeric($Variable)) die('Fehler, falsche Eingangvariable'); ------- End-Code ------- Wobei is_numeric nicht immer 100% sicher ist. - Stellen Sie nur Datenbankverbindungen mit eingeschränkten Rechten her - Eingangvariablen maskieren Die folgende Funktion maskiert die Eingangsvariable, wenn sie kein Integer ist: ------- Code ------- function quotesqlvar($value) { // Stripslashes if quoted if (get_magic_quotes_gpc()) { $value = stripslashes($value); } // Quote if not integer if (!is_numeric($value)) { $value = "'" .mysql_real_escape_string($value) . "'"; } return $value; } ------- End-Code ------- Abfrageklasse ------------------ Da das sicherste Mitel gegen SQL-Injections Typkontrolle ist, basteln wir uns eine Klasse (Idee stammt von Hanse), die eine strenge Typenkontrolle macht: Datei sec.inc: ------- Code ------- ------- End-Code ------- Eine einfache, zentrale Struktur führt oft zu einer besseren Sicherheit, hier sehen wir wie einfach die Klasse angewendet werden kann: ------- Code ------- $ArticleID = Sec::Get('articleid'); $Page = Sec::Get('page'); $Blubbfish = Sec::Get('name','Post','string'); $Test = 'sdfdsf1234'; Sec::MakeMysqlSecure($Test, 'int', 16, 100); ------- End-Code ------- weiteres Beispiel: ------- Code ------- $secure = new Sec(); $text1 = $secure->Get('text1','Post','string'); $text2 = $secure->Get('text2','Post','string'); ------- End-Code ------- Die Klasse ist natürlich noch nicht perfekt, und kann beliebig ausgebaut werden. mod_security ------------------ Als zusätzliche Massnahme gegen SQL-Injection kann ModSecurity (http://www.modsecurity.org/ projects/modsecurity/apache/index.html) als Erweiterung für den Apache verwendet werden. Unter anderem werden MySQL Anweisungen aus dem Link gefiltert. Weblinks ------------------ - PHP Handbuch (http://php3.de/manual/de/security.database.sql-injection.php) - Wikipedia Beitrag zum Thema Mysql-Injection (http://de.wikipedia.org/wiki/SQL-Injection) - Maui Security Scanner – kommerzieller Web-Anwendungs-Scanner (http://www.elanize.com) Ich meine es ist keine sogute Idee des hier so ungeschützt zu lassen da kann ja jeder Hanz Kunz i-was hinschreiben !!!