¿Cómo funciona la inyección SQL del cómic XKCD de “Bobby Tables”?

Solo mirando:

Tira XKCD (Fuente: https://xkcd.com/327/)

¿Qué hace este SQL?

Robert'); DROP TABLE STUDENTS; --

Yo se ambos ' y -- son para comentarios, pero no la palabra DROP recibir comentarios también ya que es parte de la misma línea?

preguntado el 01 de diciembre de 08 a las 19:12

Si escuchas Podcast de desbordamiento de pila n. ° 31 (27 de noviembre de 2008), en realidad discuten esto. -

En MySQL, ' no es para comentarios. Incluso si lo fuera, no hay espacio antes, por lo que solo puede terminar la cadena que lo precede. -

En lo que respecta a XKCD, si hay alguna pregunta sobre algunos de los cómics, siempre puedes ir a Explica XKCD y que averigüen su respuesta. Incluso hay un Wiki XKCD, que es muy útil para algunos cómics complicados como Geohashing XKCD -

Creo que este enlace debe registrarse aquí: bobby-tables.com -

beta.comjectedhouse.gov.uk/company/10542519 es el registro para una consultoría nombrada; TABLA DROP "EMPRESAS"; - LTD -

13 Respuestas

Deja caer la mesa de los estudiantes.

El código original en el programa de la escuela probablemente se parezca a

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

Esta es la forma ingenua de agregar entrada de texto en una consulta, y es muy mala, como verás.

Después de los valores del primer nombre, cuadro de texto del segundo nombre FNMNombre.Texto (cual es Robert'); DROP TABLE STUDENTS; --) y el cuadro de texto del apellido LName.Texto (llamémoslo Derper) están concatenados con el resto de la consulta, el resultado ahora es en realidad dos consultas separados por el terminador de declaración (punto y coma). La segunda consulta ha sido inyectado en el primero. Cuando el código ejecute esta consulta en la base de datos, se verá así

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

que, en un lenguaje sencillo, se traduce aproximadamente a las dos consultas:

Agregue un nuevo registro a la tabla Estudiantes con un valor de Nombre de 'Robert'

y

Eliminar la tabla de estudiantes

Todo lo que pasa después de la segunda consulta es marcado como comentario: --', 'Derper')

La ' a nombre del alumno no es un comentario, es el cierre delimitador de cadena. Dado que el nombre del estudiante es una cadena, se necesita sintácticamente para completar la consulta hipotética. Los ataques de inyección solo funcionan cuando la consulta SQL que inyectan da como resultado un SQL válido.

Editado de nuevo según dan04comentario astuto

contestado el 23 de mayo de 17 a las 13:05

Mmm, el DÓNDE con paréntesis alrededor de los argumentos es bastante inusual, pero al menos evita un error de sintaxis ... :-) - Filho

@PhiLho: Si la declaración original fuera un INSERT, entonces el paréntesis tendría más sentido. También explicaría por qué la conexión a la base de datos no está en modo de solo lectura. - dan04

Como explica @ dan04, el paréntesis tiene más sentido con un INSERT. Pensando al revés, el SELECT no se ejecutaría de todos modos, ya que el inserto de las Little Bobby Tables en la mesa ya habría dejado caer la mesa. - ypercubeᵀᴹ

En realidad, en este ejemplo, la primera consulta ("agregar un nuevo registro ...") fallará porque Students espera más que una sola columna (la declaración original / correcta proporcionó dos columnas). Dicho esto, la presencia de la segunda columna es útil para mostrar por qué es necesario comentar; y dado que no se puede cambiar el nombre de Bobby, probablemente sea mejor dejarlo como está con poco más que esta observación como nota al pie. - huevo

El apellido de Bobby, o al menos el de su madre, es Roberts, para Explica XKCD. Sin embargo, no estoy seguro de que corregir eso mejoraría la claridad de la respuesta. - WBT

Digamos que el nombre se usó en una variable, $Name.

Entonces ejecuta esto pregunta:

INSERT INTO Students VALUES ( '$Name' )

El código coloca por error cualquier cosa que el usuario haya proporcionado como variable.

Tu querias el SQL ser:

INSERTAR LOS VALORES DE LOS ALUMNOS ('Robert Mesas`)

Pero un usuario inteligente puede proporcionar lo que quiera:

INSERTAR LOS VALORES DE LOS ALUMNOS ('Robert '); TABLA DE GOTAS Estudiantes; -')

Lo que obtienes es:

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

La -- solo comenta el resto de la línea.

Respondido 19 Abr '20, 12:04

Esto es mucho mejor que el más votado, porque explica el paréntesis de cierre. - Tim Büthe

Por cierto, no hay forma de que el director de la escuela en los cómics se dé cuenta o el XSS, ya que se eliminó la tabla de estudiantes, no puede saber quién ha hecho esto. - xryl669

@ xryl669 Los registros son muy útiles en situaciones como esta ... A veces, todas las consultas se registran y, a veces, otra información registrada puede ayudarlo a deducir el culpable. - inemanja

Como todos los demás ya han señalado, el '); cierra la declaración original y luego sigue una segunda declaración. La mayoría de los marcos, incluidos los lenguajes como PHP, ya tienen configuraciones de seguridad predeterminadas que no permiten múltiples declaraciones en una cadena SQL. En PHP, por ejemplo, solo puede ejecutar varias sentencias en una cadena SQL utilizando el mysqli_multi_query función.

Sin embargo, puede manipular una declaración SQL existente mediante inyección SQL sin tener que agregar una segunda declaración. Digamos que tiene un sistema de inicio de sesión que verifica un nombre de usuario y una contraseña con esta simple selección:

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

Si proporciona peter como nombre de usuario y secret como contraseña, la cadena SQL resultante se vería así:

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

Todo está bien. Ahora imagine que proporciona esta cadena como contraseña:

' OR '1'='1

Entonces la cadena SQL resultante sería esta:

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

Eso le permitiría iniciar sesión en cualquier cuenta sin conocer la contraseña. Por lo tanto, no necesita poder usar dos declaraciones para usar la inyección SQL, aunque puede hacer cosas más destructivas si puede proporcionar varias declaraciones.

Respondido 20 Feb 13, 08:02

NO, ' no es un comentario en SQL, sino un delimitador.

Mamá supuso que el programador de la base de datos hizo una solicitud parecida a:

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

(por ejemplo) para agregar el nuevo estudiante, donde el $xxx El contenido de las variables se tomó directamente de un formulario HTML, sin verificar el formato ni escapar de los caracteres especiales.

Entonces si $firstName contiene Robert'); DROP TABLE students; -- el programa de la base de datos ejecutará la siguiente solicitud directamente en la base de datos:

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

es decir. terminará antes de tiempo la declaración de inserción, ejecutará cualquier código malicioso que desee el cracker y luego comentará el resto de código que pueda haber.

Mmm, soy demasiado lento, ya veo 8 respuestas antes que la mía en la banda naranja ... :-) Un tema popular, parece.

Respondido 22 Jul 14, 19:07

TL; DR

- La aplicación acepta la entrada, en este caso 'Nancy', sin intentar - desinfectar la entrada, como escapando de los caracteres especiales school => INSERT INTO estudiantes VALUES ('Nancy'); INSERT 0 1 - La inyección de SQL ocurre cuando se manipula la entrada en un comando de la base de datos para - hacer que el servidor de la base de datos ejecute SQL arbitrario school => INSERT INTO estudiantes VALUES ('Robert'); Estudiantes DROP TABLE; - '); INSERTAR 0 1 TABLA DE GOTAS - Los registros de los estudiantes ya no están, ¡podría haber sido aún peor! escuela => SELECT * FROM estudiantes; ERROR: la relación "estudiantes" no existe LÍNEA 1: SELECT * FROM estudiantes; ^

Esto elimina (elimina) la tabla de estudiantes.

(Todos los ejemplos de código en esta respuesta se ejecutaron en un servidor de base de datos PostgreSQL 9.1.2.)

Para dejar en claro lo que está sucediendo, intentemos esto con una tabla simple que contenga solo el campo de nombre y agreguemos una sola fila:

escuela => CREAR TABLA estudiantes (nombre TEXTO CLAVE PRIMARIA); AVISO: CREAR TABLA / CLAVE PRIMARIA creará un índice implícito "estudiantes_pclave" para la tabla "estudiantes" CREAR TABLA escuela => INSERTAR EN LOS VALORES DE LOS ESTUDIANTES ('John'); INSERTAR 0 1

Supongamos que la aplicación usa el siguiente SQL para insertar datos en la tabla:

INSERTAR LOS VALORES DE LOS ESTUDIANTES ('foobar');

Reemplaza foobar con el nombre real del estudiante. Una operación de inserción normal se vería así:

- Entrada: Escuela de Nancy => INSERTAR EN LOS VALORES DE LOS ESTUDIANTES ('Nancy'); INSERTAR 0 1

Cuando consultamos la tabla, obtenemos esto:

escuela => SELECT * FROM estudiantes; nombre ------- John Nancy (2 filas)

¿Qué sucede cuando insertamos el nombre de Little Bobby Tables en la mesa?

- Entrada: Robert '); Estudiantes DROP TABLE; - escuela => INSERTAR EN los estudiantes VALORES ('Robert'); Estudiantes DROP TABLE; - '); INSERTAR 0 1 TABLA DE GOTAS

La inyección de SQL aquí es el resultado del nombre del estudiante que termina la declaración e incluye una DROP TABLE mando; los dos guiones al final de la entrada están destinados a comentar cualquier código sobrante que de otro modo causaría un error. La última línea de la salida confirma que el servidor de la base de datos ha descartado la tabla.

Es importante notar que durante el INSERT operación, la aplicación no está verificando la entrada en busca de caracteres especiales y, por lo tanto, permite que se ingrese una entrada arbitraria en el comando SQL. Esto significa que un usuario malintencionado puede insertar, en un campo normalmente destinado a la entrada del usuario, símbolos especiales como comillas junto con código SQL arbitrario para hacer que el sistema de base de datos lo ejecute, por lo tanto SQL inyección.

¿El resultado?

escuela => SELECT * FROM estudiantes; ERROR: la relación "estudiantes" no existe LÍNEA 1: SELECT * FROM estudiantes; ^

La inyección SQL es la base de datos equivalente a un control remoto. ejecución de código arbitrario vulnerabilidad en un sistema operativo o aplicación. El impacto potencial de un ataque de inyección SQL exitoso no puede subestimarse: según el sistema de base de datos y la configuración de la aplicación, un atacante puede utilizarlo para provocar la pérdida de datos (como en este caso), obtener acceso no autorizado a los datos o incluso ejecutar código arbitrario en la propia máquina host.

Como señala el cómic de XKCD, una forma de protegerse contra los ataques de inyección SQL es desinfectar las entradas de la base de datos, como escapando caracteres especiales, para que no puedan modificar el comando SQL subyacente y, por lo tanto, no puedan provocar la ejecución de código SQL arbitrario. Si usa consultas parametrizadas, como al usar SqlParameter en ADO.NET, la entrada, como mínimo, se desinfectará automáticamente para protegerse contra la inyección de SQL.

Sin embargo, la desinfección de las entradas a nivel de aplicación puede no detener las técnicas de inyección SQL más avanzadas. Por ejemplo, hay formas de eludir el mysql_real_escape_string Función PHP. Para mayor protección, muchos sistemas de bases de datos admiten declaraciones preparadas. Si se implementa correctamente en el backend, las declaraciones preparadas pueden hacer que la inyección de SQL sea imposible al tratar las entradas de datos como semánticamente separadas del resto del comando.

Respondido el 20 de junio de 20 a las 10:06

Digamos que ingenuamente escribiste un método de creación de estudiantes como este:

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

Y alguien ingresa el nombre Robert'); DROP TABLE STUDENTS; --

Lo que se ejecuta en la base de datos es esta consulta:

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

El punto y coma finaliza el comando de inserción y comienza otro; el - comenta el resto de la línea. Se ejecuta el comando DROP TABLE ...

Esta es la razón por la que los parámetros de enlace son algo bueno.

Respondido el 01 de diciembre de 08 a las 21:12

Una comilla simple es el comienzo y el final de una cadena. Un punto y coma es el final de una declaración. Entonces, si estuvieran haciendo una selección como esta:

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

El SQL se convertiría en:

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

En algunos sistemas, el select sería ejecutado primero seguido por el drop ¡declaración! El mensaje es: NO INCLUYA VALORES EN SU SQL. ¡En su lugar, use parámetros!

Respondido el 15 de Septiembre de 11 a las 07:09

La '); finaliza la consulta, no inicia un comentario. Luego suelta la tabla de estudiantes y comenta el resto de la consulta que se suponía que debía ejecutarse.

Respondido 12 Abr '10, 09:04

En este caso, ' no es un personaje de comentario. Se usa para delimitar cadenas literales. El dibujante de cómics confía en la idea de que la escuela en cuestión tiene sql dinámico en algún lugar que se parece a esto:

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

Entonces ahora el ' El carácter termina la cadena literal antes de que el programador lo esperara. Combinado con el ; carácter para finalizar la declaración, un atacante ahora puede agregar (inyectar) cualquier sql que desee. La -- El comentario al final es para asegurarse de que cualquier sql restante en la declaración original no impida que la consulta se compile en el servidor.

FWIW, también creo que el cómic en cuestión tiene un detalle importante incorrecto: si Desinfectar las entradas de su base de datos, como sugiere el cómic, todavía lo está haciendo mal. En cambio, debería pensar en términos de cuarentena las entradas de su base de datos, y la forma correcta de hacerlo es a través de consultas parametrizadas / declaraciones preparadas.

respondido 03 mar '21, 18:03

El escritor de la base de datos probablemente hizo un

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

Si el nombre del alumno es el que se da, hace la selección con el nombre "Robert" y luego suelta la tabla. La parte "-" cambia el resto de la consulta dada en un comentario.

Respondido el 01 de diciembre de 08 a las 22:12

Fue mi primer pensamiento, pero aparece un error de sintaxis con el paréntesis de cierre final, ¿no? - Filho

Es por eso que hay un - al final, que indica que el texto restante es un comentario y debe ignorarse. - usuario1228

La ' El carácter en SQL se usa para constantes de cadena. En este caso, se utiliza para finalizar la constante de cadena y no para comentar.

Respondido 12 Abr '10, 09:04

Así es como funciona: supongamos que el administrador está buscando registros de estudiantes

Robert'); DROP TABLE STUDENTS; --

Dado que la cuenta de administrador tiene altos privilegios, es posible eliminar la tabla de esta cuenta.

El código para recuperar el nombre de usuario de la solicitud es

Ahora la consulta sería algo como esto (para buscar en la tabla de estudiantes)

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

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

La consulta resultante se convierte en

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

Dado que la entrada del usuario no está desinfectada, la consulta anterior se ha manipulado en 2 partes

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

DROP TABLE STUDENTS; --

El guión doble (-) solo comentará la parte restante de la consulta.

Esto es peligroso ya que puede anular la autenticación de contraseña, si está presente

El primero hará la búsqueda normal.

El segundo eliminará la mesa del estudiante si la cuenta tiene suficientes privilegios (generalmente, la cuenta de administrador de la escuela ejecutará dicha consulta y tendrá los privilegios mencionados anteriormente).

Respondido 11 Jul 16, 17:07

SELECT* FROM sutdents ... - olvidaste una "s". Esto es lo que dejas caer. DROP TABLE STUDENTS; - DevWL

No es necesario ingresar datos del formulario para realizar una inyección SQL.

Nadie señaló esto antes, así que podría alertar a algunos de ustedes.

En su mayoría, intentaremos parchear la entrada de formularios. Pero este no es el único lugar donde puede ser atacado con la inyección SQL. Puede hacer un ataque muy simple con URL que envían datos a través de la solicitud GET; Considere el siguiente ejemplo:

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

Tu url se vería http://yoursite.com/show?id=1

Ahora alguien podría intentar algo como esto

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

Intente reemplazar table_name con el nombre real de la tabla. Si obtiene el nombre correcto de su mesa, ¡la vaciaría! (Es muy fácil forzar esta URL con un script simple)

Su consulta se vería así ...

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

Ejemplo de código vulnerable PHP usando 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!!! :( *************/
...

Solución: use los métodos PDO prepare () y 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! :) *************/
...

Respondido 07 ago 19, 23:08

No es la respuesta que estás buscando? Examinar otras preguntas etiquetadas or haz tu propia pregunta.