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


Juste en regardant:

XKCD Strip (La source: https://xkcd.com/327/)

Que fait ce SQL:

Robert'); DROP TABLE STUDENTS; --

Je connais les deux ' et -- sont pour les commentaires, mais ne le mot DROP obtenir des commentaires, car il fait partie de la même ligne?


984
2017-12-01 21:50


origine


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 manière naïve d'ajouter une entrée de texte dans une requête, et très mauvais, comme vous le verrez.

Après les valeurs du prénom, zone de texte du deuxième prénom FNMName.Text (lequel est Robert'); DROP TABLE STUDENTS; --) et le champ de texte du nom LName.Text (appelons-le Derper) sont concaténées avec le reste de la requête, le résultat est maintenant deux requêtes séparé par le terminateur de déclaration (point-virgule). La deuxième requête a été injecté dans le premier. Lorsque le code exécute cette requête sur la base de données, il ressemble à ceci

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

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

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

et

Supprimer la table Etudiants

Tout passé la deuxième requête est marqué comme un commentaire: --', 'Derper')

le ' dans le nom de l'étudiant n'est pas un commentaire, c'est la fermeture délimiteur de chaîne. Étant donné que le nom de l'étudiant est une chaîne, il est nécessaire syntaxiquement de compléter la requête hypothétique. Les attaques par injection ne fonctionnent que lorsque la requête SQL qu'ils injectent résulte en SQL valide.

Édité encore selon dan04le commentaire astucieux


1018
2017-12-01 21:55



Disons que le nom a été utilisé dans une variable, $Name. Vous exécutez ensuite 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:

INSERT INTO Student VALUES ('Robert Tables`)

Mais un utilisateur intelligent peut fournir ce qu'il veut:

INSERT INTO Student VALUES ('Robert '); DROP TABLE Étudiants; -')

Ce que vous obtenez est:

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

le -- ne commente que le reste de la ligne.


546
2017-09-14 10:12



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

Vous pouvez cependant manipuler une instruction SQL existante via l'injection SQL sans devoir ajouter une seconde instruction. Disons que vous avez un système de connexion qui vérifie un nom d'utilisateur et un mot de passe avec cette simple sélection:

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

Si vous fournissez peter comme nom d'utilisateur et secret en tant que mot de passe, la chaîne SQL résultante ressemblerait à ceci:

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

Tout va bien. Imaginez maintenant que vous fournissez cette chaîne comme mot de passe:

' OR '1'='1

Alors la chaîne SQL résultante serait ceci:

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

Cela vous permettrait de vous connecter à n'importe quel compte sans connaître le mot de passe. Vous n'avez donc pas besoin de pouvoir utiliser deux instructions pour utiliser l'injection SQL, bien que vous puissiez faire des choses plus destructrices si vous êtes capable de fournir plusieurs instructions.


145
2017-12-01 22:01



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 $xxx le contenu de la variable a été extrait directement d'un formulaire HTML, sans vérifier le format ni échapper les caractères spéciaux.

Donc si $firstName contient Robert'); DROP TABLE students; -- le programme de base de données exécutera la requête suivante directement sur la base de données:

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

c'est à dire. il terminera tôt l'instruction d'insertion, exécutera tout code malveillant que le pirate veut, puis commentera tout reste de code qu'il pourrait y avoir.

Mmm, je suis trop lent, je vois déjà 8 réponses avant les miennes dans le groupe orange ... :-) Un sujet populaire, semble-t-il.


68
2017-09-26 20:20



TL; DR

- L'application accepte la saisie, en l'occurrence «Nancy», sans tenter de
- désinfecter l'entrée, par exemple en échappant aux caractères spéciaux
école => INSCRIRE DANS les étudiants VALEURS ('Nancy');
INSÉRER 0 1

- L'injection SQL se produit lorsque la saisie dans une commande de base de données est manipulée pour
- provoquer l'exécution de SQL arbitraire par le serveur de base de données
école => INSCRIRE DANS les étudiants VALEURS ('Robert'); DROP TABLE étudiants; - ');
INSÉRER 0 1
DROP TABLE

- Les dossiers des étudiants sont maintenant partis - ça aurait pu être encore pire!
école => SELECT * FROM étudiants;
ERREUR: la relation "étudiants" n'existe pas
LIGNE 1: SELECT * FROM étudiants;
                      ^

Cela supprime (supprime) la table des étudiants.

(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 ceci avec une simple table contenant seulement le champ de nom et ajoutons une seule ligne:

école => CREATE TABLE étudiants (nom TEXTE PRIMARY KEY);
AVIS: CREATE TABLE / PRIMARY KEY créera l'index implicite "students_pkey" pour la table "students"
CREATE TABLE
école => INSCRIRE DANS les étudiants VALEURS ('John');
INSÉRER 0 1

Supposons que l'application utilise le code SQL suivant pour insérer des données dans la table:

INSÉRER DANS les étudiants VALEURS ('foobar');

Remplacer foobar avec le nom réel de l'étudiant. Une opération d'insertion normale ressemblerait à ceci:

- Entrée: Nancy
école => INSCRIRE DANS les étudiants VALEURS ('Nancy');
INSÉRER 0 1

Lorsque nous interrogeons la table, nous obtenons ceci:

école => SELECT * FROM étudiants;
 prénom
-------
 John
 Nancy
(2 rangées)

Que se passe-t-il lorsque nous insérons le nom de Little Bobby Tables dans la table?

- Entrée: Robert '); DROP TABLE étudiants; -
école => INSCRIRE DANS les étudiants VALEURS ('Robert'); DROP TABLE étudiants; - ');
INSÉRER 0 1
DROP TABLE

L'injection SQL ici est le résultat du nom de l'étudiant terminant la déclaration et incluant un DROP TABLEcommander; les deux tirets à la fin de l'entrée sont destinés à commenter tout code restant qui provoquerait sinon une erreur. La dernière ligne de la sortie confirme que le serveur de base de données a supprimé la table.

Il est important de remarquer que pendant la INSERT opération l'application ne vérifie pas l'entrée pour les caractères spéciaux et permet donc d'entrer une entrée arbitraire 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 ainsi que du code SQL arbitraire pour provoquer l'exécution du système de base de données, d'où SQLinjection.

Le résultat?

école => SELECT * FROM étudiants;
ERREUR: la relation "étudiants" n'existe pas
LIGNE 1: SELECT * FROM étudiants;
                      ^

L'injection SQL est l'équivalent de la base de données d'une télécommande exécution de code arbitraire 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 provoquer une perte de données (comme dans le cas présent) code arbitraire sur la machine hôte elle-même.

Comme indiqué par la bande dessinée XKCD, une protection contre les attaques par injection SQL consiste à désinfecter les entrées de la base de données, par exemple en échappant aux caractères spéciaux, afin qu'elles ne puissent pas modifier la commande SQL sous-jacente et ne puissent pas exécuter de code SQL arbitraire. Si vous utilisez des requêtes paramétrées, par exemple en utilisant SqlParameter Dans ADO.NET, l'entrée sera, au minimum, automatiquement nettoyée pour se prémunir contre l'injection SQL.

Cependant, la désinfection des entrées au niveau de l'application peut ne pas arrêter les techniques d'injection SQL plus avancées. Par exemple, il y a des façons de contourner le 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 implémentées 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 distinctes du reste de la commande.


33
2017-12-01 21:56



Dites que vous avez naïvement écrit une méthode de création d'étudiant comme ceci:

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 d'insertion 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 liaison sont une bonne chose.


27
2017-12-01 22:03



Une seule citation est le début et la fin d'une chaîne. Un point-virgule est la fin d'une déclaration. Donc, s'ils faisaient une sélection comme ceci:

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

Le SQL deviendrait:

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

Sur certains systèmes, select serait couru d'abord suivi par le drop déclaration! Le message est le suivant: NE PAS INTRODUIRE DES VALEURS DANS VOTRE SQL. Au lieu d'utiliser des paramètres!


24
2017-12-01 21:53



le '); termine la requête, il ne démarre pas un commentaire. Ensuite, il supprime la table des étudiants et commente le reste de la requête qui devait être exécutée.


16
2017-12-01 21:55



L'auteur de la base de données a probablement fait un

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

Si student_name est celui donné, cela fait la sélection avec le nom "Robert" et ensuite abandonne la table. La partie "-" change le reste de la requête donnée en commentaire.


15
2017-12-01 21:56



Dans ce cas, 'n'est pas un caractère de commentaire. Il est utilisé pour délimiter les littéraux de chaîne. Le dessinateur de bande dessinée mise sur l'idée que l'école en question a un SQL dynamique quelque part qui ressemble à ceci:

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

Alors maintenant le caractère 'termine le littéral de chaîne avant que le programmeur ne l'attende. Combiné avec le; caractère pour mettre fin à la déclaration, un attaquant peut maintenant ajouter tout ce qu'il veut. Le - commentaire à la fin est de s'assurer que tout sql restant dans l'instruction d'origine n'empêche pas la requête de compiler sur le serveur.

FWIW, je pense aussi que la bande dessinée en question a un détail important faux: si vous songez à assainir vos entrées de base de données, comme le suggère le comic, vous le faites encore mal. Au lieu de cela, vous devriez penser en termes de mise en quarantaine vos entrées de base de données, et la manière correcte de le faire est via des requêtes paramétrées.


15
2017-12-01 21:54