¿Cómo funciona la inyección SQL de las "Bobby Tables" XKCD comic?


Solo mirando:

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

Qué hace este SQL:

Robert'); DROP TABLE STUDENTS; --

Sé que tanto ' como -- son para comentarios, pero ¿no se comenta también la palabra DROP ya que es parte de la misma línea?

Author: Floern, 2008-12-02

12 answers

Deja caer la mesa de los estudiantes.

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

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

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

Después de los valores del primer nombre, segundo nombre cuadro de texto FNMName.Text (que es Robert'); DROP TABLE STUDENTS; --) y el last name textbox LName.Text (llamémoslo Derper) se concatenan con el resto de la consulta, el resultado es ahora en realidad dos consultasseparadas por la sentencia terminator (punto y coma). La segunda consulta ha sido inyectada en la primera. Cuando el código ejecuta esta consulta contra la base de datos, se verá así

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

Que, en inglés sencillo, se traduce aproximadamente a las dos consultas:

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

Y

Eliminar los Estudiantes tabla

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

El ' en el nombre del estudiante no es un comentario, es el delimitador de cadena de cierre. 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.

Edited de nuevo as per dan04 ' s astute comentario

 1032
Author: Will,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-05-23 12:02:58

Digamos que el nombre fue usado en una variable, $Name. A continuación, ejecute esta consulta:

INSERT INTO Students VALUES ( '$Name' )

El código está colocando erróneamente cualquier cosa que el usuario suministró como la variable. Querías que el SQL fuera:

INSERTAR EN LOS estudiantes VALORES (' Tablas Robert` )

Pero un usuario inteligente puede suministrar lo que quiera:

INSERTAR EN los estudiantes VALORES ('Robert'); DROP TABLE Estudiantes; --' )

Lo que obtienes is:

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

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

 558
Author: sinoth,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-05-24 13:45:43

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 frameworks, incluidos lenguajes como PHP, tienen configuraciones de seguridad predeterminadas que no permiten múltiples sentencias en una cadena SQL. En PHP, por ejemplo, solo puede ejecutar varias sentencias en una cadena SQL usando la función mysqli_multi_query.

Sin embargo, puede manipular una instrucción SQL existente mediante inyección SQL sin tener que agregar una segunda instrucción. Supongamos que tiene un sistema de inicio de sesión que comprueba 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 la siguiente:

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. Así que no tienes que ser puede usar dos sentencias para usar la inyección SQL, aunque puede hacer cosas más destructivas si puede suministrar varias sentencias.

 146
Author: Johannes Fahrenkrug,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-02-20 08:42:21

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

Mom supuso que el programador de bases de datos hizo una solicitud como:

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

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

Así que si $firstName contiene Robert'); DROP TABLE students; -- el programa de 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');

Ie. terminará temprano el inserto sentencia, ejecutar cualquier código malicioso que el cracker quiere, a continuación, comentar cualquier resto de código que pueda haber.

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

 69
Author: PhiLho,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
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;
                      ^

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 claro lo que está sucediendo, intentemos esto con una tabla simple que contenga solo el campo nombre y agregue una sola fila:

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

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

INSERT INTO students VALUES ('foobar');

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

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

Cuando consultamos la tabla, obtenemos esto:

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

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

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

La inyección SQL aquí es el resultado del nombre del estudiante que termina la instrucción e incluye un comando DROP TABLE separado; 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 base de datos ha eliminado la tabla.

Es importante tener en cuenta que durante la operación INSERT la aplicación no está comprobando la entrada de caracteres especiales, y por lo tanto está permitiendo que la entrada arbitraria se introduzca en el comando SQL. Esto significa que un usuario malicioso 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?

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

La inyección SQL es el equivalente en la base de datos de una vulnerabilidad remota de ejecución arbitraria de código en un sistema operativo o aplicación. El impacto potencial de un ataque de inyección SQL exitoso no se puede subestimar depending dependiendo del sistema de base de datos y la configuración de la aplicación, puede ser utilizado por un atacante para causar 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 XKCD, una forma de protegerse contra los ataques de inyección SQL es desinfectar las entradas de la base de datos, por ejemplo, escapando caracteres especiales, de modo que no puedan modificar el comando SQL subyacente y, por lo tanto, no puedan causar la ejecución de código SQL arbitrario. Si utiliza consultas parametrizadas, por ejemplo, usando SqlParameter en ADO.NET, la entrada, como mínimo, se desinfectará automáticamente para proteger contra SQL inyección.

Sin embargo, la desinfección de 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 la función mysql_real_escape_string de PHP. Para mayor protección, muchos sistemas de base de datos son compatibles declaraciones preparadas. Si se implementa correctamente en el backend, las instrucciones preparadas pueden hacer imposible la inyección SQL al tratar las entradas de datos como semánticamente separadas del resto del comando.

 34
Author: bwDraco,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-06-11 03:16:49

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

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

Y alguien introduce 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 termina el comando insert e inicia otro; el comments 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.

 27
Author: Dan Vinton,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2008-12-01 21:56:45

Una comilla simple es el principio y el final de una cadena. Un punto y coma es el final de una instrucción. Así que si estaban 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 se ejecutaría primero seguido por la instrucción drop! El mensaje es: NO INCRUSTAR VALORES EN SU SQL. En lugar de utilizar parámetros!

 24
Author: CodeAndCats,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-09-15 06:39:16

El '); termina la consulta, no inicia un comentario. Luego suelta la tabla students y comenta el resto de la consulta que se suponía que se iba a ejecutar.

 16
Author: Jorn,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2010-04-12 08:18:28

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 student_name es el indicado, hace la selección con el nombre "Robert" y luego deja caer la tabla. La parte " -- " cambia el resto de la consulta dada en un comentario.

 15
Author: Paul Tomblin,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2008-12-01 22:46:58

En este caso, ' no es un carácter de comentario. Se usa para delimitar literales de cadena. El artista del cómic está apostando por la idea de que la escuela en cuestión tiene sql dinámico en algún lugar que se ve algo como esto:

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

Así que ahora el carácter ' termina el literal de cadena antes de que el programador lo esperara. Combinado con el carácter ; para finalizar la instrucción, un atacante ahora puede agregar cualquier sql que desee. El comment comment al final es para asegurarse de que cualquier sql restante en el la instrucción original no impide que la consulta se compile en el servidor.

FWIW, también creo que el cómic en cuestión tiene un detalle importante equivocado: si estás pensando en desinfectar las entradas de la base de datos, como sugiere el cómic, todavía lo estás haciendo mal. En su lugar, debe pensar en términos de poner en cuarentena las entradas de su base de datos, y la forma correcta de hacerlo es a través de consultas parametrizadas.

 15
Author: Joel Coehoorn,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-12-11 15:24:44

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

 14
Author: Rockcoder,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2010-04-12 08:17:38

Así es como funciona: Supongamos que el administrador está buscando registros de student

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 manipula en 2 partes

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

DROP TABLE STUDENTS; --

El guion doble (--) solo comentará la parte restante de la consulta.

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

El primero hará la búsqueda normal.

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

 5
Author: vivek,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-07-11 16:56:24