prevent injection example code security validation sql-injection

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



sql injection owasp (12)

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 (borra) la tabla de alumnos.

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

Para aclarar lo que está sucediendo, intentemos esto con una tabla simple que contenga solo el campo de 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 tabla?

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

La inyección de SQL aquí es el resultado del nombre del estudiante que termina la declaració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 lo contrario causaría un error. La última línea de la salida confirma que el servidor de la base de datos ha eliminado la tabla.

Es importante tener en cuenta que durante la operación INSERT , la aplicación no comprueba 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 injection .

¿El resultado?

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

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

Sin embargo, es posible que las entradas de desinfección en el nivel de la aplicación no detengan técnicas de inyección de SQL más avanzadas. Por ejemplo, hay formas de eludir la función PHP mysql_real_escape_string . 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.

Solo mirando a

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

¿Qué hace este SQL:

Robert''); DROP TABLE STUDENTS; --

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


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á saneada, la consulta anterior se manipula en 2 partes

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

El doble guión (-) 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 abandonará la tabla si la cuenta tiene suficientes privilegios (generalmente, la cuenta del administrador de la escuela ejecutará dicha consulta y tendrá los privilegios mencionados anteriormente).


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

Sin embargo, puede manipular una declaración SQL existente a través de la inyección de 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 sencilla 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 el 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 la 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 saber la contraseña. Por lo tanto, no necesita poder usar dos declaraciones para usar la inyección de SQL, aunque puede hacer cosas más destructivas si puede proporcionar varias declaraciones.


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 por error cualquier cosa que el usuario proporcionó como variable. Querías que el SQL fuera:

INSERT INTO VALORES DE LOS ESTUDIANTES ('' Robert Tables `)

Pero un usuario inteligente puede suministrar lo que quiera:

Insertar valores en los estudiantes ('' robert''); DROP TABLE Estudiantes; - '')

Lo que obtienes es:

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

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


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 entra el nombre de Robert''); DROP TABLE STUDENTS; -- 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 ...

Es por esto que los parámetros de enlace son una buena cosa.


El ''); termina la consulta, no comienza un comentario. Luego deja caer la tabla de alumnos y comenta el resto de la consulta que debía ejecutarse.


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


El escritor de la base de datos probablemente hizo una

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

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


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

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

Así que ahora el carácter ''termina la cadena literal antes de que el programador lo estuviera esperando. Combinado con el; Para finalizar la instrucción, un atacante ahora puede agregar cualquier sql que desee. 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 importante detalle erróneo: si estás pensando en sanear las entradas de tu 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 la base de datos, y la forma correcta de hacerlo es mediante consultas parametrizadas.


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

Mamá supuso que el programador de la base de datos hizo una solicitud con el siguiente aspecto:

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

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

Así que si $firstName contiene a Robert''); DROP TABLE students; -- Robert''); DROP TABLE students; -- 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á pronto la instrucción de inserción, ejecutará cualquier código malicioso que el cracker desee, luego comente el resto del 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.


Una comilla simple es el comienzo y el final de una cadena. Un punto y coma es el final de una declaración. Así que 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, la select se ejecutaría primero, seguida de la declaración de drop . El mensaje es: NO INCLUYA VALORES EN SU SQL. En su lugar usa parámetros!


Deja caer la mesa de los alumnos.

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

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 del cuadro de texto FNMName.Text (que es Robert''); DROP TABLE STUDENTS; -- Robert''); DROP TABLE STUDENTS; -- Robert''); DROP TABLE STUDENTS; -- ) y el último cuadro de texto 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 la instrucción (punto y coma). La segunda consulta ha sido inyectada en la primera. Cuando el código ejecuta 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 de Estudiantes con un valor de Nombre de ''Robert''

y

Eliminar la tabla de alumnos

Todo después de la segunda consulta se marca como un comentario : --'', ''Derper'')

El '' en el nombre del estudiante no es un comentario, es el delimitador de la 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 inyecta resultados en SQL válido .

Editado nuevamente según el astuto comentario de dan04 .