¿Cómo funciona la inyección SQL del cómic XKCD "Bobby Tables"?
Solo mirando:
(Fuente: https://xkcd.com/327/ )
¿Qué hace este SQL?
Robert'); DROP TABLE STUDENTS; --
Conozco ambos '
y --
son para comentarios, pero ¿la palabra no DROP
se comenta también ya que es parte de la misma línea?
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 a una consulta y, como verá, es muy mala .
Después de que los valores del cuadro de texto del nombre, segundo nombre FNMName.Text (que es Robert'); DROP TABLE STUDENTS; --
) y el cuadro de texto del apellido LName.Text (llamémoslo Derper
) se concatenan con el resto de la consulta, el resultado ahora son en realidad dos consultas separadas por el terminador de declaración (punto y coma). La segunda consulta se ha inyectado en la primera. 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 lenguaje sencillo, se traduce aproximadamente en 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 pase después de la segunda consulta se marca como comentario : --', 'Derper')
El '
nombre del estudiante no es un comentario, es el delimitador de cadena de cierre . Dado que el nombre del estudiante es una cadena, es necesario sintácticamente para completar la consulta hipotética. Los ataques de inyección sólo funcionan cuando la consulta SQL que inyectan da como resultado un SQL válido .
Editado de nuevo según el astuto comentario de dan04.
Digamos que el nombre se usó en una variable $Name
. Luego ejecuta esta consulta:
INSERT INTO Students VALUES ( '$Name' )
El código coloca por error cualquier cosa que el usuario haya proporcionado como variable. Querías que el SQL fuera:
INSERTAR EN VALORES de Estudiantes ( ' Robert Tables` )
Pero un usuario inteligente puede proporcionar lo que quiera:
INSERTAR EN VALORES de Estudiantes ( ' Robert'); DROP TABLE Students; --' )
Lo que obtienes es:
INSERT INTO Students VALUES ( 'Robert' ); DROP TABLE STUDENTS; --' )
El --
único comenta el resto de la línea.
Como todos los demás ya han señalado, ');
cierra la declaración original y luego sigue una segunda declaración. La mayoría de los marcos, incluidos lenguajes como PHP, ya tienen configuraciones de seguridad predeterminadas que no permiten múltiples declaraciones en una cadena SQL. En PHP, por ejemplo, sólo puedes ejecutar múltiples declaraciones en una cadena SQL usando la 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 tienes 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
el nombre de usuario y secret
la contraseña, la cadena SQL resultante se vería así:
SELECT * FROM users WHERE username='peter' and (password='secret')
Todo está bien. Ahora imagina que proporcionas 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 es necesario poder utilizar dos declaraciones para utilizar la inyección SQL, aunque puede hacer cosas más destructivas si puede proporcionar varias declaraciones.
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 esta:
INSERT INTO 'students' ('first_name', 'last_name') VALUES ('$firstName', '$lastName');
(por ejemplo) para agregar el nuevo estudiante, donde el $xxx
contenido variable se tomó directamente de un formulario HTML, sin verificar el formato ni escapar de caracteres especiales.
Entonces, 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');
es decir. Terminará antes de tiempo la instrucción de inserción, ejecutará cualquier código malicioso que el cracker desee y luego comentará cualquier resto de código que pueda haber.
Mmm, soy demasiado lento, ya veo 8 respuestas antes que la mía en la banda naranja... :-) Parece que es un tema popular.
TL;DR
-- La aplicación acepta entradas, en este caso 'Nancy', sin intentar -- desinfectar la entrada, por ejemplo, escapando caracteres especiales escuela=> INSERTAR EN LOS VALORES de los estudiantes ('Nancy'); INSERTAR 0 1 -- La inyección SQL ocurre cuando la entrada en un comando de base de datos se manipula para -- hace que el servidor de la base de datos ejecute SQL arbitrario escuela=> INSERTAR EN LOS VALORES de los estudiantes ('Robert'); estudiantes de DROP TABLE; --'); INSERTAR 0 1 MESA PLEGABLE -- Los registros de los estudiantes ya no están; ¡podría haber sido aún peor! escuela=> SELECCIONAR * DE estudiantes; ERROR: la relación "estudiantes" no existe LÍNEA 1: SELECCIONAR * DE 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 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 "students_pkey" para la tabla "students" CREAR MESA escuela=> INSERTAR EN LOS VALORES de los estudiantes ('John'); INSERTAR 0 1
Supongamos que la aplicación utiliza el siguiente SQL para insertar datos en la tabla:
INSERTAR EN LOS VALORES de los estudiantes ('foobar');
Reemplace foobar
con el nombre real del estudiante. Una operación de inserción normal se vería así:
-- Entrada: Nancy escuela=> INSERTAR EN LOS VALORES de los estudiantes ('Nancy'); INSERTAR 0 1
Cuando consultamos la tabla, obtenemos esto:
escuela=> SELECCIONAR * DE estudiantes; nombre ------- John nancy (2 filas)
¿Qué sucede cuando insertamos el nombre de Little Bobby Tables en la tabla?
-- Entrada: Robert'); estudiantes de DROP TABLE; -- escuela=> INSERTAR EN LOS VALORES de los estudiantes ('Robert'); estudiantes de DROP TABLE; --'); INSERTAR 0 1 MESA PLEGABLE
La inyección SQL aquí es el resultado de que el nombre del estudiante termine la declaración e incluya un DROP TABLE
comando separado; Los dos guiones al final de la entrada tienen como objetivo comentar cualquier código sobrante que, de otro modo, causaría un error. La última línea del resultado confirma que el servidor de la base de datos ha eliminado la tabla.
Es importante tener en cuenta que durante la INSERT
operación la aplicación no verifica la entrada en busca de caracteres especiales y, por lo tanto, permite ingresar entradas arbitrarias 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 y, por lo tanto, la inyección SQL .
¿El resultado?
escuela=> SELECCIONAR * DE estudiantes; ERROR: la relación "estudiantes" no existe LÍNEA 1: SELECCIONAR * DE estudiantes; ^
La inyección SQL es el equivalente en la base de datos de una vulnerabilidad de ejecución remota de código arbitrario en un sistema operativo o aplicación. No se puede subestimar el impacto potencial de un ataque de inyección SQL exitoso: dependiendo del 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 señala en 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, por ejemplo, mediante caracteres especiales de escape, para que no puedan modificar el comando SQL subyacente y, por lo tanto, no puedan provocar la ejecución de código SQL arbitrario. Esto se puede hacer a nivel de aplicación, y algunas implementaciones de consultas parametrizadas operan desinfectando la entrada.
Sin embargo, es posible que desinfectar las entradas a nivel de aplicación no detenga las técnicas de inyección SQL más avanzadas. Por ejemplo, existen formas de eludir la mysql_real_escape_string
función PHP . Para mayor protección, muchos sistemas de bases de datos admiten declaraciones preparadas . Si se implementan correctamente en el backend, las declaraciones preparadas pueden hacer que la inyección SQL sea imposible al tratar las entradas de datos como semánticamente separadas del resto del comando.