1219 votes

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

Je regarde juste :

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

Que fait ce SQL :

Robert'); DROP TABLE STUDENTS; --

Je connais les deux ' et -- sont destinés aux commentaires, mais le mot DROP doit-il être commenté également puisqu'il fait partie de la même ligne ?

18 votes

Si vous écoutez Podcast Stack Overflow n°31 (27 novembre 2008), ils en parlent en fait.

102 votes

Dans MySQL, ' n'est pas pour commentaires . Même s'il l'était, il n'y a pas d'espace avant lui, il ne peut donc que terminer la chaîne qui le précède.

52 votes

En ce qui concerne XKCD, si vous avez des questions sur certains des comics, vous pouvez toujours aller sur Expliquer XKCD et vous aurez votre réponse. Il y a même un XKCD wiki ce qui est très utile pour certaines bandes dessinées difficiles comme XKCD geohashing

1200voto

Will Points 76760

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 + "')";

Il s'agit de la manière naïve d'ajouter une entrée de texte dans une requête, et elle est très mauvais comme vous le verrez.

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

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

ce qui, en clair, se traduit approximativement par les deux requêtes :

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

et

Supprimer la table Students

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

Le site ' dans le nom de l'élève n'est pas un commentaire, c'est la conclusion délimiteur de chaîne . Le nom de l'étudiant étant une chaîne, il est nécessaire d'un point de vue syntaxique 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. .

Modifié à nouveau selon dan04 Commentaire perspicace de l'auteur

3 votes

Mmm, le WHERE avec des parenthèses autour des arguments est plutôt inhabituel, mais au moins il évite une erreur de syntaxe... :-)

66 votes

@PhiLho : Si la déclaration originale était un INSERT alors la parenthèse aurait plus de sens. Cela expliquerait également pourquoi la connexion à la base de données n'est pas en mode lecture seule.

4 votes

Comme l'explique @dan04, la parenthèse a plus de sens avec une INSERT . En réfléchissant à rebours, le SELECT ne s'exécuterait pas de toute façon car l'insertion des tables Little Bobby dans la table aurait déjà fait tomber la table.

667voto

sinoth Points 3657

Disons que le nom a été utilisé dans une variable, $Name .

Vous exécutez ensuite ceci requête :

INSERT INTO Students VALUES ( '$Name' )

Le code place par erreur tout ce que l'utilisateur a fourni comme variable.

Vous vouliez le SQL pour être :

INSERT INTO Students VALUES ( ' Robert Tables ` )

Mais un utilisateur astucieux peut fournir tout ce qu'il veut :

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

Ce que vous obtenez est :

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

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

96 votes

C'est bien mieux que le vote le plus élevé, car cela explique la parenthèse finale.

2 votes

D'ailleurs, le directeur de l'école dans la bande dessinée n'a aucun moyen d'être au courant du XSS puisque la table des étudiants est supprimée, il ne peut pas savoir qui a fait cela.

2 votes

@xryl669 Les journaux sont très utiles dans des situations comme celle-ci... Parfois toutes les requêtes sont enregistrées, et parfois d'autres informations enregistrées peuvent vous aider à déduire le coupable.

175voto

Johannes Fahrenkrug Points 12795

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

Vous pouvez toutefois manipuler une instruction SQL existante par injection SQL sans devoir ajouter une deuxième 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 comme mot de passe, la chaîne SQL résultante ressemblerait à ceci :

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

Tout va bien. Maintenant, imaginez que vous fournissez cette chaîne de caractères comme mot de passe :

' OR '1'='1

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

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. Il n'est donc pas nécessaire de pouvoir utiliser deux instructions pour utiliser l'injection SQL, bien que vous puissiez faire des choses plus destructives si vous êtes en mesure de fournir plusieurs instructions.

73voto

PhiLho Points 23458

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

Maman suppose que le programmeur de la 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 élève, où l'option $xxx Le contenu de la variable a été extrait directement d'un formulaire HTML, sans vérification du format ni échappement des caractères spéciaux.

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

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

c'est-à-dire qu'il se terminera au début de l'instruction d'insertion, exécutera le code malveillant que le pirate souhaite, puis commentera le reste du code qu'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, semble-t-il.

42voto

DragonLord Points 625

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;
                      ^

Cela 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 bien comprendre ce qui se passe, essayons avec un tableau simple contenant uniquement 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 la table :

INSERT INTO students VALUES ('foobar');

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

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

Lorsque 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 le tableau ?

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

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

Il est important de noter que pendant la INSERT l'application ne vérifie pas la présence de caractères spéciaux dans la saisie et autorise donc la saisie de données arbitraires dans la commande SQL. Cela signifie qu'un utilisateur malveillant peut insérer, dans un champ normalement destiné à la saisie par l'utilisateur, des symboles spéciaux tels que des guillemets ainsi qu'un code SQL arbitraire pour que le système de base de données l'exécute, d'où SQL  injection .

Le résultat ?

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

L'injection SQL est l'équivalent pour la base de données d'une attaque à distance 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 doit pas être sous-estimé. Selon la configuration du système de base de données et de l'application, un attaquant peut l'utiliser pour provoquer une perte de données (comme dans le cas présent), obtenir un accès non autorisé aux données, voire exécuter un code arbitraire sur la machine hôte elle-même.

Comme l'indique la bande dessinée XKCD, l'un des moyens de se protéger contre les attaques par injection SQL consiste à nettoyer les entrées de la base de données, par exemple en échappant les caractères spéciaux, afin qu'elles ne puissent pas modifier la commande SQL sous-jacente et donc provoquer l'exécution d'un code SQL arbitraire. Cela peut être fait au niveau de l'application, et certaines implémentations de requêtes paramétrées fonctionnent en aseptisant les entrées.

Cependant, l'assainissement des entrées au niveau de l'application peut ne pas arrêter les techniques d'injection SQL plus avancées. Par exemple, il existe des moyens 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 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.

0 votes

Les paramètres SqlParameters sont non aseptisé Ils sont juste interprétés comme des données uniquement, et jamais comme du code. C'est la façon correcte de faire les choses, garder les données et le code séparés.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X