Wie funktioniert die SQL-Injection aus dem XKCD-Comic „Bobby Tables“?


1093

Nur anschauen:

XKCD-Streifen (Quelle: https://xkcd.com/327/ )

Was macht diese SQL:

Robert'); DROP TABLE STUDENTS; --

Ich kenne beide 'und bin --für Kommentare, aber wird das Wort DROPnicht auch kommentiert, da es Teil derselben Zeile ist?


16
Wenn Sie sich den Stapelüberlauf-Podcast Nr. 31 (27. November 2008) anhören , wird dies tatsächlich besprochen.
EBGreen

93
In MySQL 'ist nicht für Kommentare . Selbst wenn dies der Fall wäre, steht kein Leerzeichen davor, sodass nur die vorhergehende Zeichenfolge beendet werden kann.
Leichtigkeitsrennen im Orbit

45
Was XKCD betrifft, können Sie bei Fragen zu einigen Comics jederzeit XKCD erklären und Ihre Antwort herausfinden lassen. Es gibt sogar ein XKCD-Wiki , das für einige knifflige Comics wie XKCD-Geohashing
Anatoli

13
Ich glaube, dieser Link muss hier aufgezeichnet werden: bobby-tables.com
Arioch 'The

2
beta.companieshouse.gov.uk/company/10542519 ist die Registrierung für ein Beratungsunternehmen mit dem Namen; DROP TABLE "COMPANIES"; - LTD
Alex Dupuy

Antworten:


1116

Es lässt den Schülertisch fallen.

Der ursprüngliche Code im Schulprogramm sieht wahrscheinlich so aus

q = "INSERT INTO Students VALUES ('" + FNMName.Text + "', '" + LName.Text + "')";

Dies ist die naive Methode zum Hinzufügen von Texteingaben zu einer Abfrage und , wie Sie sehen werden, sehr schlecht .

Nachdem die Werte aus dem Vornamen, dem zweiten Vornamen-Textfeld FNMName.Text (das ist Robert'); DROP TABLE STUDENTS; --) und dem Nachnamen-Textfeld LName.Text (nennen wir es Derper) mit dem Rest der Abfrage verknüpft sind, sind das Ergebnis nun tatsächlich zwei durch das getrennt Anweisungsabschluss (Semikolon). Die zweite Abfrage wurde in die erste eingefügt . Wenn der Code diese Abfrage für die Datenbank ausführt, sieht sie folgendermaßen aus

INSERT INTO Students VALUES ('Robert'); DROP TABLE Students; --', 'Derper')

was im Klartext grob die beiden Abfragen übersetzt:

Fügen Sie der Schülertabelle einen neuen Datensatz mit dem Wert "Robert" hinzu.

und

Löschen Sie die Schülertabelle

Alles nach der zweiten Abfrage wird als Kommentar markiert : --', 'Derper')

Das 'im Namen des Schülers ist kein Kommentar, sondern das abschließende Zeichenfolgenbegrenzer . Da der Name des Schülers eine Zeichenfolge ist, wird er syntaktisch benötigt, um die hypothetische Abfrage abzuschließen. Injection-Angriffe funktionieren nur, wenn die von ihnen injizierte SQL-Abfrage zu gültigem SQL führt .

Herausgegeben wieder nach dan04 ‚s klugen Kommentar


3
Mmm, das WHERE mit Klammern um die Argumente ist eher ungewöhnlich, aber es vermeidet zumindest einen Syntaxfehler ... :-)
PhiLho

60
@PhiLho: Wenn die ursprüngliche Aussage eine INSERTwäre, wäre die Klammer sinnvoller. Es würde auch erklären, warum die Datenbankverbindung nicht schreibgeschützt ist.
Dan04

3
Wie @ dan04 erklärt, ist die Klammer mit einem sinnvoller INSERT. Wenn man rückwärts denkt, SELECTwürde das sowieso nicht laufen, da das Einfügen der kleinen Bobby-Tische in die Tabelle die Tabelle bereits fallen gelassen hätte.
Ypercubeᵀᴹ

10
In diesem Beispiel schlägt die erste Abfrage ("Neuen Datensatz hinzufügen ...") fehl, da Studentsmehr als nur eine Spalte erwartet wird (die ursprüngliche / korrekte Anweisung enthält zwei Spalten). Das Vorhandensein der zweiten Spalte ist jedoch hilfreich, um zu zeigen, warum Kommentare erforderlich sind. und da man Bobbys Namen nicht ändern kann, ist es wahrscheinlich am besten, so wie er ist mit wenig mehr als dieser Beobachtung als Fußnote zu belassen.
Eggyal

7
Bobbys Nachname - oder zumindest der seiner Mutter - ist Roberts , laut Explain XKCD . Ich bin mir jedoch nicht sicher, ob eine Korrektur die Klarheit der Antworten verbessern würde.
WBT

611

Angenommen, der Name wurde in einer Variablen verwendet $Name.

Anschließend führen Sie diese Abfrage aus :

INSERT INTO Students VALUES ( '$Name' )

Der Code platziert fälschlicherweise alles, was der Benutzer als Variable angegeben hat.

Sie wollten, dass SQL :

IN Schülerwerte EINFÜGEN (' Robert Tables`)

Aber ein kluger Benutzer kann liefern, was er will:

IN Schülerwerte EINFÜGEN (' Robert'); DROP TABLE Students; --')

Was Sie bekommen, ist:

INSERT INTO Students VALUES ( 'Robert' );  DROP TABLE STUDENTS; --' )

Der --einzige kommentiert den Rest der Zeile.


87
Dies ist viel besser als das am höchsten gewählte, da es die schließende Klammer erklärt.
Tim Büthe

1
Übrigens gibt es für den Schulleiter in den Comics keine Möglichkeit, sich des XSS bewusst zu werden, da der Schülertisch gelöscht wird. Er kann nicht wissen, wer dies getan hat.
xryl669

@ xryl669 Protokolle sind in solchen Situationen sehr hilfreich ... Manchmal werden alle Abfragen protokolliert, und manchmal können andere protokollierte Informationen Ihnen helfen, den Schuldigen abzuleiten.
Inemanja

165

Wie alle anderen bereits betont haben, ');schließt das die ursprüngliche Aussage und dann folgt eine zweite Aussage. Die meisten Frameworks, einschließlich Sprachen wie PHP, verfügen derzeit über Standardsicherheitseinstellungen, die nicht mehrere Anweisungen in einer SQL-Zeichenfolge zulassen. In PHP können Sie beispielsweise mit der mysqli_multi_queryFunktion nur mehrere Anweisungen in einer SQL-Zeichenfolge ausführen .

Sie können jedoch eine vorhandene SQL-Anweisung über SQL Injection bearbeiten, ohne eine zweite Anweisung hinzufügen zu müssen. Angenommen, Sie haben ein Anmeldesystem, das einen Benutzernamen und ein Passwort mit dieser einfachen Auswahl überprüft:

$query="SELECT * FROM users WHERE username='" . $_REQUEST['user'] . "' and (password='".$_REQUEST['pass']."')";
$result=mysql_query($query);

Wenn Sie peterals Benutzernamen und secretals Kennwort angeben, sieht die resultierende SQL-Zeichenfolge folgendermaßen aus:

SELECT * FROM users WHERE username='peter' and (password='secret')

Alles ist gut. Stellen Sie sich nun vor, Sie geben diese Zeichenfolge als Kennwort an:

' OR '1'='1

Dann wäre die resultierende SQL-Zeichenfolge folgende:

SELECT * FROM users WHERE username='peter' and (password='' OR '1'='1')

Auf diese Weise können Sie sich bei jedem Konto anmelden, ohne das Kennwort zu kennen. Sie müssen also nicht in der Lage sein, zwei Anweisungen zu verwenden, um SQL-Injection zu verwenden, obwohl Sie destruktivere Dinge tun können, wenn Sie mehrere Anweisungen bereitstellen können.


71

Nein, 'ist kein Kommentar in SQL, sondern ein Trennzeichen.

Mama vermutete, dass der Datenbankprogrammierer eine Anfrage gestellt hatte, die wie folgt aussah:

INSERT INTO 'students' ('first_name', 'last_name') VALUES ('$firstName', '$lastName');

(zum Beispiel) um den neuen Schüler hinzuzufügen, bei dem der $xxxvariable Inhalt direkt aus einem HTML-Formular entnommen wurde, ohne das Format zu überprüfen oder Sonderzeichen zu umgehen.

Wenn also das Datenbankprogramm $firstNameenthält Robert'); DROP TABLE students; --, wird die folgende Anforderung direkt in der Datenbank ausgeführt:

INSERT INTO 'students' ('first_name', 'last_name') VALUES ('Robert'); DROP TABLE students; --', 'XKCD');

dh. Die Anweisung insert wird vorzeitig beendet, der vom Cracker gewünschte schädliche Code ausgeführt und der verbleibende Code auskommentiert.

Mmmm, ich bin zu langsam, ich sehe schon 8 Antworten vor mir in der orangefarbenen Band ... :-) Ein beliebtes Thema, wie es scheint.


39

TL; DR

- Die Anwendung übernimmt Eingang, in diesem Fall ‚Nancy‘, ohne zu versuchen - sterilisieren Sie die Eingabe, wie durch die Flucht Sonderzeichen 
Schule => INSERT INTO Studenten VALUES ( ‚Nancy‘ ); INSERT 0 1
   
  

- SQL - Injektion auftritt , wenn die Eingabe in eine Datenbank Befehl manipuliert wird - Ursache des Datenbankserver beliebigen SQL auszuführen 
Schule => INSERT INTO Studenten VALUES ( ‚Robert‘ ); DROP TABLE Studenten ; - '); INSERT 0 1 DROP TABLE
      
  
 

- Die Studentenakten sind jetzt weg - es hätte noch schlimmer kommen können! 
Schule => SELECT * FROM Schüler ; 
ERROR :   Beziehung „Schüler“ ist nicht vorhanden   
Zeile 1 : SELECT * FROM Studenten ; ^   
                      

Dadurch wird die Schülertabelle gelöscht (gelöscht).

( Alle Codebeispiele in dieser Antwort wurden auf einem PostgreSQL 9.1.2-Datenbankserver ausgeführt. )

Um zu verdeutlichen, was passiert, versuchen wir dies mit einer einfachen Tabelle, die nur das Namensfeld enthält, und fügen eine einzelne Zeile hinzu:

Schule => CREATE TABLE Schüler ( Name TEXT PRIMARY KEY ); 
HINWEIS : CREATE TABLE / PRIMARY KEY wird schaffen impliziten Index "students_pkey" für Tabelle "Studenten" CREATE TABLE 
Schule => INSERT INTO Studenten VALUES ( 'John' ); INSERT 0 1             
    
  

Angenommen, die Anwendung verwendet das folgende SQL, um Daten in die Tabelle einzufügen:

INSERT INTO Studenten VALUES ( 'Foobar' );  

Ersetzen Sie foobardurch den tatsächlichen Namen des Schülers. Ein normaler Einfügevorgang würde folgendermaßen aussehen:

- Eingabe: Nancy 
Schule => EINFÜGEN IN DIE WERTE DER Schüler ( 'Nancy' ); INSERT 0 1   
  

Wenn wir die Tabelle abfragen, erhalten wir Folgendes:

Schule => SELECT * FROM Schüler ;   
 Name
-------
 John
 Nancy
( 2 Reihen ) 

Was passiert, wenn wir den Namen von Little Bobby Tables in die Tabelle einfügen?

- Eingabe: Robert '); DROP TABLE Studenten; - 
Schule => INSERT INTO Studenten VALUES ( 'Robert' ); DROP TABLE Studenten ; - '); INSERT 0 1 DROP TABLE      
  
 

Die SQL-Injection ist hier das Ergebnis des Namens des Schülers, der die Anweisung beendet und einen separaten DROP TABLEBefehl enthält. Die beiden Striche am Ende der Eingabe sollen alle verbleibenden Codes auskommentieren, die sonst einen Fehler verursachen würden. Die letzte Zeile der Ausgabe bestätigt, dass der Datenbankserver die Tabelle gelöscht hat.

Es ist wichtig zu beachten, dass INSERTdie Anwendung während des Vorgangs die Eingabe nicht auf Sonderzeichen überprüft und daher die Eingabe beliebiger Eingaben in den SQL-Befehl ermöglicht. Dies bedeutet, dass ein böswilliger Benutzer in ein Feld, das normalerweise für Benutzereingaben vorgesehen ist, spezielle Symbole wie Anführungszeichen und beliebigen SQL-Code einfügen kann, um das Datenbanksystem zur Ausführung zu veranlassen, daher SQL-  Injection .

Das Ergebnis?

Schule => SELECT * FROM Schüler ; 
ERROR :   Beziehung „Schüler“ ist nicht vorhanden   
Zeile 1 : SELECT * FROM Studenten ; ^   
                      

SQL Injection ist das Datenbankäquivalent einer Sicherheitsanfälligkeit bezüglich der Ausführung von beliebigem Remotecode in einem Betriebssystem oder einer Anwendung. Die möglichen Auswirkungen eines erfolgreichen SQL-Injection-Angriffs sind nicht zu unterschätzen. Je nach Datenbanksystem und Anwendungskonfiguration kann ein Angreifer Datenverluste verursachen (wie in diesem Fall), unbefugten Zugriff auf Daten erhalten oder sogar ausführen beliebiger Code auf dem Host-Computer selbst.

Wie im XKCD-Comic erwähnt, besteht eine Möglichkeit zum Schutz vor SQL-Injection-Angriffen darin, Datenbankeingaben zu bereinigen, z. B. indem Escapezeichen maskiert werden, damit sie den zugrunde liegenden SQL-Befehl nicht ändern und daher keinen beliebigen SQL-Code ausführen können. Wenn Sie parametrisierte Abfragen verwenden, z. B. SqlParameterin ADO.NET, wird die Eingabe mindestens automatisch bereinigt, um eine SQL-Injektion zu verhindern.

Das Bereinigen von Eingaben auf Anwendungsebene kann jedoch fortgeschrittenere SQL-Injektionstechniken nicht stoppen. Beispielsweise gibt es Möglichkeiten, die mysql_real_escape_stringPHP-Funktion zu umgehen . Für zusätzlichen Schutz unterstützen viele Datenbanksysteme vorbereitete Anweisungen . Bei ordnungsgemäßer Implementierung im Backend können vorbereitete Anweisungen die SQL-Injection unmöglich machen, indem Dateneingaben als semantisch vom Rest des Befehls getrennt behandelt werden.


30

Angenommen, Sie haben naiv eine Schülererstellungsmethode wie diese geschrieben:

void createStudent(String name) {
    database.execute("INSERT INTO students (name) VALUES ('" + name + "')");
}

Und jemand gibt den Namen ein Robert'); DROP TABLE STUDENTS; --

Was in der Datenbank ausgeführt wird, ist diese Abfrage:

INSERT INTO students (name) VALUES ('Robert'); DROP TABLE STUDENTS --')

Das Semikolon beendet den Einfügebefehl und startet einen anderen. the - kommentiert den Rest der Zeile aus. Der Befehl DROP TABLE wird ausgeführt ...

Aus diesem Grund sind Bindungsparameter eine gute Sache.


26

Ein einfaches Anführungszeichen ist der Anfang und das Ende einer Zeichenfolge. Ein Semikolon ist das Ende einer Anweisung. Wenn sie also eine Auswahl wie diese treffen würden:

Select *
From Students
Where (Name = '<NameGetsInsertedHere>')

Das SQL würde werden:

Select *
From Students
Where (Name = 'Robert'); DROP TABLE STUDENTS; --')
--             ^-------------------------------^

Auf einigen Systemen wird das selectzuerst ausgeführt, gefolgt von der dropAnweisung! Die Meldung lautet: VERBINDEN SIE KEINE WERTE IN IHR SQL. Verwenden Sie stattdessen Parameter!


18

Das ');beendet die Abfrage, es startet keinen Kommentar. Dann wird die Schülertabelle gelöscht und der Rest der Abfrage kommentiert, die ausgeführt werden sollte.


17

Der Verfasser der Datenbank hat wahrscheinlich eine

sql = "SELECT * FROM STUDENTS WHERE (STUDENT_NAME = '" + student_name + "') AND other stuff";
execute(sql);

Wenn student_name angegeben ist, führt dies die Auswahl mit dem Namen "Robert" durch und löscht dann die Tabelle. Der Teil "-" ändert den Rest der angegebenen Abfrage in einen Kommentar.


Es war mein erster Gedanke, aber Sie erhalten einen Syntaxfehler mit der nachfolgenden schließenden Klammer, nicht wahr?
PhiLho

3
Aus diesem Grund gibt es am Ende ein -, das angibt, dass der verbleibende Text ein Kommentar ist und ignoriert werden sollte.

17

In diesem Fall ist 'kein Kommentarzeichen. Es wird verwendet, um Zeichenfolgenliterale abzugrenzen. Der Comiczeichner setzt auf die Idee, dass die betreffende Schule irgendwo ein dynamisches SQL hat, das ungefähr so ​​aussieht:

$sql = "INSERT INTO `Students` (FirstName, LastName) VALUES ('" . $fname . "', '" . $lname . "')";

Jetzt beendet das Zeichen 'das Zeichenfolgenliteral, bevor der Programmierer es erwartet hat. Kombiniert mit dem; Um die Anweisung zu beenden, kann ein Angreifer nun beliebiges SQL hinzufügen. Der - Kommentar am Ende soll sicherstellen, dass die verbleibende SQL in der ursprünglichen Anweisung die Kompilierung der Abfrage auf dem Server nicht verhindert.

FWIW, ich denke auch das Comic in Frage ein wichtiges Detail falsch ist: Wenn Sie denken über desinfizierende Ihre Datenbank - Eingänge, wie der Comic schon sagt, du bist immer noch etwas falsch gemacht. Stattdessen sollten Sie über die Quarantäne Ihrer Datenbankeingaben nachdenken. Der richtige Weg, dies zu tun, sind parametrisierte Abfragen.


16

Das 'Zeichen in SQL wird für Zeichenfolgenkonstanten verwendet. In diesem Fall wird es zum Beenden der Zeichenfolgenkonstante und nicht zum Kommentieren verwendet.


7

So funktioniert es: Nehmen wir an, der Administrator sucht nach Aufzeichnungen über Schüler

Robert'); DROP TABLE STUDENTS; --

Da das Administratorkonto über hohe Berechtigungen verfügt, ist das Löschen der Tabelle aus diesem Konto möglich.

Der Code zum Abrufen des Benutzernamens aus der Anforderung lautet

Jetzt wäre die Abfrage ungefähr so ​​(um die Schülertabelle zu durchsuchen)

String query="Select * from student where username='"+student_name+"'";

statement.executeQuery(query); //Rest of the code follows

Die resultierende Abfrage wird

Select * from student where username='Robert'); DROP TABLE STUDENTS; --

Da die Benutzereingabe nicht bereinigt ist, wird die obige Abfrage in zwei Teile zerlegt

Select * from student where username='Robert'); 

DROP TABLE STUDENTS; --

Der doppelte Bindestrich (-) kommentiert nur den verbleibenden Teil der Abfrage aus.

Dies ist gefährlich, da die Kennwortauthentifizierung, falls vorhanden, ungültig werden kann

Der erste führt die normale Suche durch.

Der zweite löscht den Tischschüler, wenn das Konto über ausreichende Berechtigungen verfügt (im Allgemeinen führt das Schuladministratorkonto eine solche Abfrage aus und verfügt über die oben genannten Berechtigungen).


SELECT* FROM sutdents ...- Du hast ein "s" vergessen. Das ist es, was du fallen lässt. DROP TABLE STUDENTS;
DevWL

4

Sie müssen keine Formulardaten eingeben, um eine SQL-Injection durchzuführen.

Niemand hat zuvor darauf hingewiesen, so dass ich einige von Ihnen alarmieren könnte.

Meistens werden wir versuchen, Formulareingaben zu patchen. Dies ist jedoch nicht der einzige Ort, an dem Sie mit SQL-Injection angegriffen werden können. Sie können sehr einfache Angriffe mit URLs ausführen, die Daten über eine GET-Anforderung senden. Betrachten Sie das folgende Beispiel:

<a href="/show?id=1">show something</a>

Ihre URL würde http://yoursite.com/show?id=1 aussehen

Jetzt könnte jemand so etwas versuchen

http://yoursite.com/show?id=1;TRUNCATE table_name

Versuchen Sie, table_name durch den echten Tabellennamen zu ersetzen. Wenn er Ihren Tischnamen richtig macht, würden sie Ihren Tisch leeren! (Es ist sehr einfach, diese URL mit einem einfachen Skript zu erzwingen.)

Ihre Anfrage würde ungefähr so ​​aussehen ...

"SELECT * FROM page WHERE id = 4;TRUNCATE page"

Beispiel für PHP-anfälligen Code mit PDO:

<?php
...
$id = $_GET['id'];

$pdo = new PDO($database_dsn, $database_user, $database_pass);
$query = "SELECT * FROM page WHERE id = {$id}";
$stmt = $pdo->query($query);
$data = $stmt->fetch(); 
/************* You have lost your data!!! :( *************/
...

Lösung - Verwenden Sie die Methoden PDO prepare () & bindParam ():

<?php
...
$id = $_GET['id'];

$query = 'SELECT * FROM page WHERE id = :idVal';
$stmt = $pdo->prepare($query);
$stmt->bindParam('idVal', $id, PDO::PARAM_INT);
$stmt->execute();
$data = $stmt->fetch();
/************* Your data is safe! :) *************/
...
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.