Tutorials - SQL-Injection
Sprachenübersicht/Programmierung/PHP/Security
SQL-Injection
Diese Seite wurde 102541 mal aufgerufen.
Dieser Artikel wurde in einem Wikiweb System geschrieben, das heißt, Sie können die Artikel jederzeit editieren, wenn Sie einen Fehler gefunden haben, oder etwas hinzufügen wollen.
Editieren Versionen Linkpartnerschaft Bottom Printversion
Keywords: SQL-Injection, mysql, php
Inhaltsverzeichnis
In letzter Zeit häufen sich die Meldungen über SQL-Injection Lücken in bekannten 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.
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, dass 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...
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']);
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:
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"
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']);
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.
Die wichtigste Verteidigung gegen Mysql-Injections ist das überprüfen der Eingangsvariablen. 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'
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');
Mit der Funktion is_numeric kann man überprüfen ob eine Variable Zeichen enthält:
Code:
if(is_numeric($Variable)) die('Fehler, falsche Eingangvariable');
Wobei is_numeric nicht immer 100% sicher ist.
- Stellen Sie nur Datenbankverbindungen mit eingeschränkten Rechten her
- Werte maskieren
Mit der Funktion mysql_real_escape_string bietet PHP eine gute Möglichkeit Variablen vor dem Einsetzen in eine SQL Query zu maskieren.
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:
<?php
class Sec
{
function MakeMysqlSecure($Variable, $Type, $Min = 0, $Max = 10000)
{
switch($Type)
{
case 'int':
settype($Variable, 'Integer');
if($Variable < $Min || $Variable > $Max)
$Variable = 0;
break;
case 'string':
$Variable = mysql_real_escape_string($Variable);
break;
case 'float':
settype($Variable, 'float');
if($Variable < $Min || $Variable > $Max)
$Variable = 0;
break;
case 'bool':
settype($Variable, 'boolean');
break;
default:
return 0;
}
return $Variable;
}
function MakeMysqlArraySecure($Variable, $Type, $Min = 0, $Max = 10000)
{
foreach ($Variable as $key => $value)
$Variable[$key] = Sec::MakeMysqlSecure($value ,$Type, $Min, $Max);
return $Variable;
}
function Get($Variable, $GetType = 'Get', $Type = 'int', $Min = 0, $Max = 100000)
{
switch($GetType)
{
case 'Post':
$Variable = $_POST[$Variable];
break;
case 'Get':
$Variable = $_GET[$Variable];
break;
default:
return 0;
}
$Variable = Sec::MakeMysqlSecure($Variable, $Type, $Min, $Max);
return $Variable;
}
}
?>
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);
weiteres Beispiel:
Code:
$secure = new Sec();
$text1 = $secure->Get('text1','Post','string');
$text2 = $secure->Get('text2','Post','string');
Die Klasse ist natürlich noch nicht perfekt, und kann beliebig ausgebaut werden.
Als zusätzliche Massnahme gegen SQL-Injection kann ModSecurity als Erweiterung für den Apache verwendet werden. Unter anderem werden MySQL Anweisungen aus dem Link gefiltert.
Gibt es noch irgendwelche Fragen, oder wollen Sie über den Artikel diskutieren?
Editieren Versionen Linkpartnerschaft Top Printversion
Haben Sie einen Fehler gefunden? Dann klicken Sie doch auf Editieren, und beheben den Fehler, keine Angst, Sie können nichts zerstören, das Tutorial kann wiederhergestellt werden
Sprachenübersicht/Programmierung/PHP/Security/SQL-Injection