Comment fonctionne l’injection SQL à partir de la bande dessinée «Bobby Tables» XKCD?

Juste en regardant:

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

Que fait ce SQL:

Robert'); DROP TABLE STUDENTS; -- 

Je sais les deux ' et -- sont pour les commentaires, mais le mot DROP n’est-il pas commenté puisqu’il fait partie de la même ligne?

    Il laisse tomber la table des étudiants.

    Le code d’origine 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’append du texte à une requête, ce qui est très grave , comme vous le verrez.

    Après les valeurs du prénom, nom du fichier textbox FNMName.Text (qui est Robert'); DROP TABLE STUDENTS; -- Robert'); DROP TABLE STUDENTS; -- Robert'); DROP TABLE STUDENTS; -- ) et le textbox de nom de famille LName.Text (appelons-le Derper ) sont concaténés avec le rest de la requête, le résultat est maintenant en réalité deux requêtes séparées par le terminateur d’instruction (point-virgule). La deuxième requête a été injectée dans le premier. Lorsque le code exécute cette requête sur la firebase database, cela ressemblera à ceci

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

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

    Ajouter un nouvel enregistrement à la table Etudiants avec une valeur Nom de ‘Robert’

    et

    Supprimer la table des étudiants

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

    Le nom de l’étudiant n’est pas un commentaire, c’est le délimiteur de chaîne de fermeture. Étant donné que le nom de l’étudiant est une chaîne, il est nécessaire d’avoir une syntaxe pour compléter la requête hypothétique. Les attaques par injection ne fonctionnent que lorsque la requête SQL qu’elles injectent génère du code SQL valide .

    Edité à nouveau selon le commentaire astucieux de dan04

    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:

    INSÉRER DANS LES VALEURS DES ÉLÈVES (‘ Robert Tables `)

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

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

    Ce que vous obtenez est:

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

    Le -- ne commente que le rest de la ligne.

    Comme tout le monde l’a déjà fait remarquer, le '); ferme la déclaration d’origine et une seconde instruction suit. La plupart des frameworks, y compris des langages tels que PHP, ont désormais des parameters 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 la fonction mysqli_multi_query .

    Vous pouvez toutefois manipuler une instruction SQL existante via une injection SQL sans avoir à append 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 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 fournissiez cette chaîne comme mot de passe:

     ' OR '1'='1 

    La chaîne SQL obtenue serait alors 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. Vous n’avez donc pas besoin de pouvoir utiliser deux instructions pour utiliser l’injection SQL, bien que vous puissiez faire des choses plus destrucsortingces si vous êtes en mesure de fournir plusieurs instructions.

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

    Maman a supposé que le programmeur de firebase database avait fait une requête ressemblant à ceci:

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

    (par exemple) pour append le nouvel étudiant, où le contenu de la variable $xxx été pris directement à partir d’un formulaire HTML, sans vérifier le format ni échapper les caractères spéciaux.

    Donc si $firstName contient Robert'); DROP TABLE students; -- Robert'); DROP TABLE students; -- Robert'); DROP TABLE students; -- le programme de firebase database exécute la requête suivante directement sur la firebase database:

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

    c’est à dire. il mettra fin à l’instruction insert, exécutera le code malveillant souhaité par le pirate, puis commentera le rest du code.

    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.

    TL; DR

     - L’application accepte les entrées, dans ce cas «Nancy», sans tenter de
     - désinfecter l’entrée, par exemple en évitant les caractères spéciaux
     school => INSERT INTO étudiants VALEURS ('Nancy');
     INSERT 0 1
    
     - L'injection SQL se produit lorsque l'entrée dans une commande de firebase database est manipulée pour
     - obliger le serveur de firebase database à exécuter du code SQL arbitraire
     school => INSERT INTO étudiants VALEURS ('Robert');  DROP TABLE étudiants;  - ')
     INSERT 0 1
     DROP TABLE
    
     - Les dossiers des étudiants ont maintenant disparu - ça aurait pu être encore pire!
     école => SELECT * des étudiants;
     ERREUR: la relation "étudiants" n'existe pas
     LIGNE 1: SELECT * DES ÉTUDIANTS;
                           ^
    

    Cela supprime la table des élèves.

    ( Tous les exemples de code de cette réponse ont été exécutés sur un serveur de firebase database PostgreSQL 9.1.2. )

    Pour clarifier ce qui se passe, essayons ceci avec un simple tableau contenant uniquement le champ name et en ajoutant une seule ligne:

     school => Étudiants CREATE TABLE (nom de TEXT PRIMARY KEY);
     AVIS: CREATE TABLE / PRIMARY KEY créera un index implicite "students_pkey" pour les "étudiants" de la table
     CREER LA TABLE
     school => INSERT INTO étudiants VALEURS ('John');
     INSERT 0 1
    

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

     INSERT INTO étudiants VALEURS ('foobar');
    

    Remplacez foobar par le nom de l’élève. Une opération d’insertion normale ressemblerait à ceci:

     - Entrée: Nancy
     school => INSERT INTO étudiants VALEURS ('Nancy');
     INSERT 0 1
    

    Lorsque nous interrogeons la table, nous obtenons ceci:

     école => SELECT * des étudiants;
      prénom
     -------
      John
      Nancy
     (2 lignes)
    

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

     - Entrée: Robert ');  DROP TABLE étudiants;  -
     school => INSERT INTO étudiants VALEURS ('Robert');  DROP TABLE étudiants;  - ')
     INSERT 0 1
     DROP TABLE
    

    L’injection SQL est le résultat du nom de l’étudiant qui termine l’instruction et inclut une commande DROP TABLE distincte. les deux tirets à la fin de la saisie sont destinés à mettre en commentaire tout code restant qui provoquerait une erreur. La dernière ligne de la sortie confirme que le serveur de firebase database a supprimé 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 d’entrer des entrées arbitraires dans la commande SQL. Cela signifie qu’un utilisateur malveillant peut insérer, dans un champ normalement destiné à une entrée utilisateur, des symboles spéciaux tels que des guillemets avec du code SQL arbitraire pour que le système de firebase database l’exécute, d’où l’ injection SQL .

    Le résultat?

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

    L’injection SQL est l’équivalent d’une firebase database d’une vulnérabilité d’ exécution de code arbitraire à distance 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é – en fonction du système de firebase database et de la configuration de l’application, un attaquant peut utiliser des attaques non autorisées pour accéder aux données ou même les exécuter. code arbitraire sur la machine hôte elle-même.

    Comme indiqué par la bande dessinée XKCD, une façon de se protéger contre les attaques par injection SQL consiste à assainir les entrées de la firebase database, en évitant les caractères spéciaux, de manière à ne pouvoir modifier la commande SQL sous-jacente. Si vous utilisez des requêtes paramétrées, par exemple en utilisant SqlParameter dans ADO.NET, l’entrée sera au moins automatiquement désinfectée pour empêcher l’injection SQL.

    Cependant, la désinfection des entrées au niveau de l’application peut ne pas arrêter des techniques d’injection SQL plus avancées. Par exemple, il existe des moyens de contourner la fonction PHP mysql_real_escape_ssortingng . Pour plus de protection, de nombreux systèmes de firebase database prennent en charge les instructions 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 séparées du rest de la commande.

    Disons que vous avez naïvement écrit une méthode de création d’étudiants comme ceci:

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

    Et quelqu’un entre le nom de Robert'); DROP TABLE STUDENTS; -- Robert'); DROP TABLE STUDENTS; --

    Ce qui est exécuté sur la firebase database est cette requête:

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

    Le point-virgule termine la commande d’insertion et en commence une autre; the – commente le rest de la ligne. La commande DROP TABLE est exécutée …

    C’est pourquoi les parameters de liaison sont une bonne chose.

    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 un choix comme celui-ci:

     Select * From Students Where (Name = '') 

    Le SQL deviendrait:

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

    Sur certains systèmes, le select serait exécuté en premier, suivi de l’instruction drop ! Le message est le suivant: DONT EMBED VALUES IN YOUR SQL. Utilisez plutôt des parameters!

    Le '); termine la requête, il ne commence pas un commentaire. Ensuite, il abandonne la table des étudiants et commente le rest de la requête qui devait être exécutée.

    L’auteur de la firebase database a probablement fait un

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

    Si nom_etudiant est celui donné, cela fait la sélection avec le nom “Robert”, puis supprime la table. La partie “-” modifie le rest de la requête en un commentaire.

    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 compte sur l’idée que l’école en question a un sql dynamic 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 append le SQL de son choix. 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 comstackr sur le serveur.

    FWIW, je pense aussi que la bande dessinée en question a un détail important: si vous envisagez de nettoyer vos entrées de firebase database, comme le suggère la bande dessinée, vous vous trompez toujours. Au lieu de cela, vous devriez penser en termes de mise en quarantaine de vos entrées de firebase database, et la manière correcte de le faire via des requêtes paramétrées.

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

    Voici comment cela fonctionne: Supposons que l’administrateur recherche des enregistrements d’étudiants

     Robert'); DROP TABLE STUDENTS; -- 

    Étant donné que le compte admin a des privilèges élevés, la suppression de 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 ça (pour rechercher la table des étudiants)

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

    Comme la saisie de l’utilisateur n’est pas assainie, la requête ci-dessus est manipulée en 2 parties

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

    Le double tiret (-) ne fait que commenter la partie restante de la requête.

    Ceci est dangereux car il peut annuler l’authentification par mot de passe, le cas échéant

    Le premier effectuera la recherche normale.

    Le second déposera l’étudiant de la table si le compte dispose de privilèges suffisants (généralement, le compte administrateur de l’école exécutera une telle requête et aura les privilèges mentionnés ci-dessus).