SQL-Injection
    
     SQL-Injection ist eine Technik, bei der ein Angreifer Schwachstellen im
     Anwendungscode ausnutzt, mit dem dynamische SQL-Abfragen erstellt werden.
     Ein Angreifer kann sich Zugang zu besonders geschützten Bereichen der
     Anwendung verschaffen, sämtliche Informationen aus der Datenbank abrufen,
     vorhandene Daten manipulieren oder sogar gefährliche Befehle auf der
     Systemebene des Datenbank-Hosts ausführen. Die Schwachstelle entsteht,
     wenn Entwickler in ihren SQL-Anweisungen beliebige Eingaben miteinander
     verknüpfen oder einfügen.
    
    
     
      Beispiel #1 
       Die Ergebnisliste in mehrere Seiten aufsplitten ... und Superuser anlegen
       (PostgreSQL)
      
      
       Im folgenden Beispiel wird die Benutzereingabe direkt in die
       SQL-Abfrage eingefügt, wodurch der Angreifer ein Superuser-Konto für
       die Datenbank erlangen kann.
      
<?php
$offset = $_GET['offset']; // Vorsicht, keine Validierung der Eingabe!
$query  = "SELECT id, name FROM products ORDER BY name LIMIT 20 OFFSET $offset;";
$result = pg_query($conn, $query);
?>
       
      
     Normale Benutzer klicken auf die 'nächste'- bzw. 'vorige'-Links, wo der
     
$offset in der 
URL enthalten ist.
     Das Skript erwartet, dass der ankommende 
$offset eine
     Zahl enthält. Was aber, wenn jemand versucht einzubrechen, indem er das
     Folgende an die 
URL anhängt:
     
     In diesem Fall würde das Skript dem Angreifer einen Superuser-Zugang zur
     Verfügung stellen. Beachten Sie, dass 
0; einen
     gültigen Offset zur ursprünglichen Abfrage liefert, und sie beendet.
    
    
Hinweis: 
     
      Es ist eine übliche Technik, den SQL-Parser mit dem SQL-Kommentarzeichen
      -- zu zwingen, den Rest der vom Entwickler
      geschriebenen Abfrage zu ignorieren.
     
    
    
     Eine gangbare Methode, an Passwörter zu gelangen, ist, Ihre Seiten mit
     den Suchergebnissen zu umgehen. Der Angreifer braucht nur zu probieren,
     ob irgendeine übertragene Variable, die in dem SQL-Statement verwendet
     wird, nicht richtig gehandhabt wird. Diese Filter können gewöhnlich in
     einem vorausgehenden Formular gesetzt werden, indem WHERE-,
     ORDER BY-, LIMIT- und OFFSET-Klauseln in
     SELECT-Statements angepasst werden. Wenn Ihre
     Datenbank das UNION-Konstrukt unterstützt, kann der
     Angreifer versuchen, eine komplette Abfrage an das Original anzuhängen,
     um Passwörter aus einer beliebigen Tabelle aufzulisten. Es wird dringend
     empfohlen, nur sichere Hashes von Passwörtern zu speichern und nicht die
     Passwörter selbst.
     
      Beispiel #2 
       Artikel auflisten ... und ein paar Passwörter (beliebiger Datenbankserver)
      
      
<?php
$query  = "SELECT id, name, inserted, size FROM products
           WHERE size = '$size'";
$result = odbc_exec($conn, $query);
?>
       
      
     Der statische Teil der Abfrage kann mit einem anderen
     
SELECT-Statement kombiniert werden, welches alle
     Passwörter preisgibt
     
    
    
     Auch UPDATE- und INSERT-Anweisungen
     sind für derartige Angriffe anfällig.
     
      Beispiel #3 
       Vom Zurücksetzen eines Passworts ... zum Erlangen von mehr Rechten
       (beliebiger Datenbankserver)
      
      
<?php
$query = "UPDATE usertable SET pwd='$pwd' WHERE uid='$uid';";
?>
       
      
     Wenn ein böswilliger Benutzer den Wert 
' or uid
     like'%admin% an 
$uid übermittelt, um das
     Administrator-Passwort zu ändern, oder einfach 
$pwd
     auf 
hehehe', trusted=100, admin='yes setzt, um mehr
     Rechte zu erhalten, dann wird die Abfrage verdreht:
     
    
    
     Obwohl es offensichtlich ist, dass ein Angreifer zumindest ein wenig
     Kenntnis der genutzten Datenbankarchitektur besitzen muss, um einen
     erfolgreichen Angriff durchzuführen, ist es oft sehr einfach, diese
     Informationen zu erhalten. Der Code kann z. B. Teil einer
     Open-Source-Software sein und öffentlich zugänglich sein. Diese
     Informationen können auch durch Closed-Source-Code preisgegeben werden -
     selbst wenn dieser kodiert, verschleiert oder kompiliert ist - und sogar
     durch Ihren eigenen Code durch die Anzeige von Fehlermeldungen. Andere
     Methoden beinhalten die Nutzung typischer Tabellen- und Spaltennamen. Ein
     Login-Formular etwa, dass eine Tabelle 'users' mit den Spaltennamen 'id',
     'username' und 'password' nutzt.
    
    
     
      Beispiel #4 Angriff auf das Betriebssystem des Datenbank-Hosts (MSSQL-Server)
      
       Ein erschreckendes Beispiel, wie der Zugriff auf Befehle auf
       Betriebssystemebene bei manchen Datenbankservern erfolgen kann:
      
<?php
$query  = "SELECT * FROM products WHERE id LIKE '%$prod%'";
$result = mssql_query($query);
?>
       
      
     Wenn ein Angreifer den Wert 
a%' exec master..xp_cmdshell 'net
     user test testpass /ADD' -- auf 
$prod
     überträgt, wird 
$query zu:
     
     Der MSSQL-Server führt die SQL-Statements im Batch aus, inklusive eines
     Kommandos um einen neuen Benutzer zur Datenbank der lokalen Konten
     hinzuzufügen. Würde diese Anwendung als 
sa und der
     MSSQLSERVER-Service mit genügend Rechten laufen, hätte der Angreifer nun
     ein Konto, mit welchem er Zugriff auf diese Maschine hätte.
    
    
Hinweis: 
     
      Manche der obigen Beispiele sind an einen spezifischen Datenbankserver
      gebunden, aber das bedeutet nicht, dass ein ähnlicher Angriff auf andere
      Produkte unmöglich wäre. Ihr Datenbankserver könnte auf andere Weise
      genauso verwundbar sein.
     
    
    
     
    
    
     Techniken zur Vermeidung
     
      Um das Risiko einer SQL-Injection zu verringern, wird empfohlen, alle
      Daten über vorbereitete Anweisungen zu binden. Die Verwendung von
      parametrisierten Abfragen ist zwar nicht ausreichend, um SQL-Injections
      vollständig zu verhindern, aber es ist der einfachste und sicherste Weg,
      um Daten für SQL-Anweisungen bereitzustellen. Alle dynamischen
      Datenliterale in WHERE-, SET- und
      VALUES-Klauseln müssen durch Platzhalter ersetzt
      werden. Die eigentlichen Daten werden bei der Ausführung gebunden und
      getrennt vom SQL-Befehl gesendet.
     
     
      Die Parameterbindung kann nur für Daten verwendet werden. Andere
      dynamische Teile der SQL-Abfrage müssen gegen eine bekannte Liste
      zulässiger Werte gefiltert werden.
     
     
      
       Beispiel #5 Ein sicherer Weg, eine Abfrage zu erstellen
       
<?php
// Der dynamische SQL-Teil wird anhand der erwarteten Werte validiert
$sortingOrder = $_GET['sortingOrder'] === 'DESC' ? 'DESC' : 'ASC';
$productId = $_GET['productId'];
// Das SQL wird mit einem Platzhalter vorbereitet
$stmt = $pdo->prepare("SELECT * FROM products WHERE id LIKE ? ORDER BY price {$sortingOrder}");
// Der Wert wird mit LIKE-Wildcards versehen
$stmt->execute(["%{$productId}%"]);
?>
        
       
     
     
      Vorbereitete Anweisungen werden von
      PDO,
      MySQLi und
      anderen Bibliotheken angeboten.
     
     
      Angriffe durch SQL-Injection basieren hauptsächlich darauf, Code
      auszunutzen, der nicht unter dem Aspekt der Sicherheit geschrieben
      wurde. Vertrauen Sie niemals einer Eingabe, insbesondere nicht von der
      Clientseite, selbst wenn sie von einer Auswahlbox, einem versteckten
      Eingabefeld oder einem Cookie kommt. Das erste Beispiel zeigt, dass eine
      so einfache Abfrage zu einer Katastrophe führen kann.
     
     
      Eine umfassende Verteidigungsstrategie umfasst mehrere bewährte
      Programmierpraktiken:
      
       - 
        
         Stellen Sie nie als Superuser oder Eigentümer einer Datenbank eine
         Verbindung zur Datenbank her. Verwenden Sie immer speziell angelegte
         Benutzer mit sehr limitierten Rechten.
        
       
- 
        
         Prüfen Sie, ob die gegebene Eingabe dem erwarteten Datentyp
         entspricht. PHP bietet eine große Auswahl an
         Funktionen zum Validieren der Eingaben, von den einfachsten unter
         Variablenfunktionen und
         Character-Type-Funktionen (z. B.
         is_numeric(), bzw.
         ctype_digit()), bis hin zu den
         Perl-kompatiblen regulären Ausdrücken.
        
       
- 
        
         Wenn die Anwendung eine numerische Eingabe erwartet, sollten Sie die
         Daten mittels ctype_digit() überprüfen, den Typ
         stillschweigend mittels settype() ändern oder
         deren numerische Darstellung mittels sprintf()
         verwenden.
        
       
- 
        
         Unterstützt die Datenbank-Ebene das Binden von Variablen nicht, so
         maskieren Sie jede nichtnumerische Benutzereingabe, welche zur
         Datenbank weitergereicht werden soll, mit der jeweiligen
         datenbankspezifischen Maskierungsfunktion für Zeichenketten (z. B.
         mysql_real_escape_string(),
         sqlite_escape_string() usw.). Generische Funktionen
         wie addslashes() sind nur in einer sehr
         spezifischen Umgebung nützlich (z. B. MySQL mit einem
         SingleByte-Zeichensatz bei deaktiviertem
         NO_BACKSLASH_ESCAPES), sodass es besser ist, diese
         zu vermeiden.
        
       
- 
        
         Geben Sie um keinen Preis irgendwelche datenbankspezifischen
         Informationen aus, speziell über das Schema. Siehe auch
         Fehlerbehandlung und
         Fehlerbehandlungsfunktionen.
        
       
      Abgesehen davon profitieren Sie von einer Protokollierung der Abfragen
      entweder in Ihrem Skript oder durch die Datenbank selbst, falls sie
      Protokollierung unterstützt. Klar, die Protokollierung kann keinen
      schädlichen Versuch verhindern, aber sie kann helfen herauszufinden,
      welche Anwendung umgangen wurde. Das Protokoll ist nicht von sich aus
      nützlich, aber durch die in ihm enthaltenen Informationen, und je mehr
      Details es enthält, desto besser ist es im Allgemeinen.