Comment fonctionne L'injection SQL de la BD" Bobby Tables " XKCD?

Simplement en la regardant:

XKCD Strip (Source: https://xkcd.com/327/ )

Qu'est-ce SQL n':

Robert'); DROP TABLE STUDENTS; --

je sais que les deux ' et -- sont pour les commentaires, mais ne possède pas le mot DROP get a commenté ainsi depuis qu'elle est partie de la même ligne?

1002
demandé sur Floern 2008-12-02 00:50:10

12 réponses

il laisse tomber la table des étudiants.

le code original dans le programme de l'école ressemble probablement à quelque chose comme

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

C'est la façon naïve d'ajouter du texte dans une requête, et est très mauvais , comme vous le verrez.

après les valeurs du prénom, deuxième nom textbox FNMName.Texte (qui est Robert'); DROP TABLE STUDENTS; -- ) et le nom de famille textbox nom.Texte (appelons-le Derper ) sont concaténés avec le reste de la requête, le résultat est maintenant en fait deux requêtes séparé par le terminateur de l'instruction (point-virgule). La seconde requête a été injecté dans la première. Quand le code exécute cette requête contre la base de données, il ressemblera à ceci

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

qui, en anglais, se traduit grossièrement par les deux requêtes:

Ajouter un nouvel enregistrement à la table Students avec une valeur de nom de 'Robert'

et

supprimer le tableau des étudiants

tout après la deuxième requête est marqué comme un commentaire : --', 'Derper')

le ' au nom de l'étudiant n'est pas un commentaire, c'est la fermeture délimiteur de chaîne de caractères . Puisque le nom de l'étudiant est une chaîne de caractères, il est nécessaire syntaxiquement pour compléter la requête hypothétique. Les attaques par Injection ne fonctionnent que lorsque la requête SQL qu'ils injectent donne un résultat SQL valide .

Édité nouveau que par dan04 's astucieux commentaire

1032
répondu Will 2017-05-23 12:02:58

disons que le nom a été utilisé dans une variable, $Name . Vous lancez alors cette requête:

INSERT INTO Students VALUES ( '$Name' )

le code place par erreur tout ce que l'Utilisateur a fourni comme variable. Vous vouliez que le SQL soit:

inscrire les valeurs (' Robert Tables `)

mais un utilisateur intelligent peut fournir tout ce qu'il veut:

INSERT INTO Étudiants VALUES ( ' Robert'); DROP TABLE Élèves; -- ' )

Ce que vous obtenez est:

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

le -- commente seulement le reste de la ligne.

558
répondu sinoth 2018-05-24 13:45:43

comme tout le monde l'a déjà souligné, le '); ferme la déclaration originale et puis une deuxième déclaration suit. La plupart des cadres, y compris les langues comme PHP, ont maintenant des paramètres de sécurité par défaut qui n'autorisent pas de déclarations multiples dans une chaîne SQL. En PHP, par exemple, vous ne pouvez exécuter plusieurs instructions que dans une chaîne SQL en utilisant la fonction mysqli_multi_query .

vous pouvez, cependant, manipuler une déclaration SQL existante par injection SQL sans avoir à ajouter un deuxième état. Supposons que vous ayez un système de login qui vérifie un nom d'utilisateur et un mot de passe avec ce simple select:

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

si vous donnez peter comme nom d'utilisateur et secret comme mot de passe, la chaîne de caractères SQL résultante ressemblera à ceci:

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

tout va bien. Maintenant, imaginez que vous fournissez cette chaîne comme le mot de passe:

' OR '1'='1

puis la chaîne SQL résultante serait-ce:

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

qui vous permettrait de vous connecter à n'importe quel compte sans connaître le mot de passe. Ainsi, vous n'avez pas besoin d'être en mesure d'utiliser deux instructions pour utiliser L'injection SQL, bien que vous puissiez faire des choses plus destructrices si vous êtes en mesure de fournir plusieurs instructions.

146
répondu Johannes Fahrenkrug 2013-02-20 08:42:21

Non, ' n'est pas un commentaire en SQL, mais un délimiteur.

Maman a supposé que le programmeur de base de données a fait une demande ressemblant à:

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

(par exemple) pour ajouter le nouvel étudiant, où le contenu de la variable $xxx a été retiré directement d'un formulaire HTML, sans vérifier le format ni échapper aux caractères spéciaux.

donc si $firstName contient Robert'); DROP TABLE students; -- le programme de base de données exécutera le suivant demande directement sur le DB:

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

ie. il terminera tôt la déclaration insert, exécutera n'importe quel code malveillant que le pirate veut, puis commentera dehors n'importe quel reste de code il pourrait y avoir.

Mmm, je suis trop lent, je vois déjà 8 réponses avant la mienne dans la bande orange... :-) Un sujet populaire, il semble.

69
répondu PhiLho 2014-07-22 18:34:30

TL; DR

-- The application accepts input, in this case 'Nancy', without attempting to
-- sanitize the input, such as by escaping special characters
school=> INSERT INTO students VALUES ('Nancy');
INSERT 0 1

-- SQL injection occurs when input into a database command is manipulated to
-- cause the database server to execute arbitrary SQL
school=> INSERT INTO students VALUES ('Robert'); DROP TABLE students; --');
INSERT 0 1
DROP TABLE

-- The student records are now gone - it could have been even worse!
school=> SELECT * FROM students;
ERROR:  relation "students" does not exist
LINE 1: SELECT * FROM students;
                      ^

Cette baisse (supprime) les étudiants de la table.

( tous les exemples de code dans cette réponse ont été exécutés sur un serveur de base de données PostgreSQL 9.1.2. )

pour clarifier ce qui se passe, essayons avec une table simple contenant seulement le champ Nom et ajoutons une seule ligne:

school=> CREATE TABLE students (name TEXT PRIMARY KEY);
NOTICE:  CREATE TABLE / PRIMARY KEY will create implicit index "students_pkey" for table "students"
CREATE TABLE
school=> INSERT INTO students VALUES ('John');
INSERT 0 1

supposons que l'application utilise le SQL suivant pour insérer des données dans le tableau:

INSERT INTO students VALUES ('foobar');

remplacer foobar par le nom réel de l'élève. Une opération normale d'insertion ressemblerait à ceci:

--                            Input:   Nancy
school=> INSERT INTO students VALUES ('Nancy');
INSERT 0 1

quand nous interrogeons la table, nous obtenons ceci:

school=> SELECT * FROM students;
 name
-------
 John
 Nancy
(2 rows)

que se passe-t-il quand on insère le nom de Little Bobby Tables dans la table?

--                            Input:   Robert'); DROP TABLE students; --
school=> INSERT INTO students VALUES ('Robert'); DROP TABLE students; --');
INSERT 0 1
DROP TABLE

l'injection SQL voici le résultat du nom de l'élève mettant fin à l'énoncé et incluant une commande séparée DROP TABLE ; les deux tirets à la fin de l'entrée sont destinés à commenter tout code restant qui causerait autrement une erreur. La dernière ligne de la sortie confirme que le serveur de base de données a laissé tomber la table.

il est important de noter que pendant l'opération INSERT l'application ne vérifie pas l'entrée pour les caractères spéciaux, et permet donc l'entrée arbitraire à entrer dans la commande SQL. Cela signifie qu'un utilisateur malveillant peut insérer, dans un champ normalement destiné à l'entrée de l'Utilisateur, des symboles spéciaux tels que des guillemets avec le code SQL arbitraire pour provoquer le système de base de données pour l'exécuter, donc SQL injection .

le résultat?

school=> SELECT * FROM students;
ERROR:  relation "students" does not exist
LINE 1: SELECT * FROM students;
                      ^

SQL injection est l'équivalent en base de données d'une exécution de code arbitraire à distance Vulnérabilité dans un système d'exploitation ou une application. L'impact potentiel d'une attaque par injection SQL réussie ne peut pas être sous-estimé--selon le système de base de données et la configuration de l'application, il peut être utilisé par un attaquant pour causer une perte de données (comme dans ce cas), obtenir un accès non autorisé aux données, ou même exécuter du code arbitraire sur la machine hôte elle-même.

comme le note la bande dessinée XKCD, une façon de se protéger contre les attaques par injection SQL est de assainir les entrées de la base de données, par exemple en échappant aux caractères spéciaux, de sorte qu'ils ne puissent pas modifier la commande SQL sous-jacente et donc ne puissent pas causer l'exécution de code SQL arbitraire. Si vous utilisez des requêtes paramétrées, comme en utilisant SqlParameter dans ADO.NET, l'entrée sera, au minimum, automatiquement aseptisée pour se prémunir contre L'injection SQL.

cependant, la désinfection des entrées au niveau de l'application ne peut pas arrêter L'injection plus avancée de SQL technique. Par exemple, il y a des moyens de contourner la mysql_real_escape_string fonction PHP . Pour une protection supplémentaire, de nombreux systèmes de bases de données prennent en charge "déclarations préparées . Si elles sont correctement mises en œuvre dans le backend, les instructions préparées peuvent rendre L'injection SQL impossible en traitant les entrées de données comme sémantiquement séparées du reste de la commande.

34
répondu bwDraco 2018-06-11 03:16:49

dites que vous avez naïvement écrit une méthode de création étudiante comme celle-ci:

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

et quelqu'un entre le nom Robert'); DROP TABLE STUDENTS; --

ce qui est exécuté sur la base de données est cette requête:

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

le point-virgule termine la commande insert et en démarre une autre; le -- commente le reste de la ligne. La commande DROP TABLE est exécutée...

c'est pourquoi les paramètres de bind sont une bonne chose.

27
répondu Dan Vinton 2008-12-01 21:56:45

Une seule citation est le début et la fin d'une chaîne. Un point-virgule est la fin d'une instruction. Donc, s'ils faisaient un select comme celui-ci:

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

le SQL deviendrait:

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

sur certains systèmes, le select serait lancé en premier suivi de la déclaration drop ! Le message est: ne pas intégrer des valeurs dans votre SQL. Utilisez plutôt les paramètres!

24
répondu CodeAndCats 2011-09-15 06:39:16

le '); termine la requête, il ne démarre pas de commentaire. Puis il laisse tomber la table des étudiants et commente le reste de la requête qui était censée être exécutée.

16
répondu Jorn 2010-04-12 08:18:28

L'écrivain de la base de données n'a probablement un

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

si student_name est celui donné, qui fait la sélection avec le nom" Robert " et puis laisse tomber la table. La partie " -- " change le reste de la requête en commentaire.

15
répondu Paul Tomblin 2008-12-01 22:46:58

Dans ce cas, " n'est pas un caractère de commentaire. Il est utilisé pour délimiter les chaînes de caractères littérales. L'artiste comique mise sur l'idée que l'école en question a un sql dynamique quelque part qui ressemble à quelque chose comme ceci:

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

donc maintenant le caractère finit la chaîne littérale avant que le programmeur ne l'attende. Combiné avec le caractère ; pour terminer la déclaration, un attaquant peut maintenant ajouter n'importe quel sql qu'il veut. Le -- commentaire à la fin est de s'assurer tout sql restant dans la déclaration originale n'empêche pas la requête de se compiler sur le serveur.

FWIW, je pense aussi que le comique en question a un détail important erroné: si vous pensez à assainir vos entrées de base de données, comme le comique suggère, vous le faites toujours mal. Au lieu de cela, vous devriez penser en termes de mise en quarantaine vos entrées de base de données, et la bonne façon de le faire est via des requêtes paramétrées.

15
répondu Joel Coehoorn 2014-12-11 15:24:44

le caractère ' en SQL est utilisé pour les constantes de chaîne. Dans ce cas, il est utilisé pour terminer la constante de chaîne et non pour commenter.

14
répondu Rockcoder 2010-04-12 08:17:38

C'est comme ça que ça marche: Supposons que l'administrateur est à la recherche des dossiers de l'étudiant

Robert'); DROP TABLE STUDENTS; --

puisque le compte administrateur a des privilèges élevés supprimer la table de ce compte est possible.

le code pour récupérer le nom d'utilisateur de la demande est

maintenant la requête serait quelque chose comme ceci (pour rechercher la table d'étudiant)

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

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

la requête résultante devient

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

puisque l'entrée de l'utilisateur n'est pas aseptisée, la requête ci-dessus a été manipulée en 2 parties

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

DROP TABLE STUDENTS; --

le double tiret (--) commente juste la partie restante de la requête.

c'est dangereux car cela peut annuler l'authentification par mot de passe, si elle existe.""

le premier effectuera la recherche normale.

le deuxième laissera tomber la table étudiant si le compte a suffisant privilèges (en Général, l'école compte administrateur exécuter cette requête et auront les privilèges parlé ci-dessus).

5
répondu vivek 2016-07-11 16:56:24