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



Einführung Top



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.

Angriffsmöglichkeiten von SQL-Injections Top



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...

Szenarien Top



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.



Gegenmaßnahmen Top



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.



Abfrageklasse Top



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.

mod_security Top



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.

Weblinks Top




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