Inyección SQL

La versión actual de la página aún no ha sido revisada por colaboradores experimentados y puede diferir significativamente de la versión revisada el 19 de diciembre de 2021; las comprobaciones requieren 9 ediciones .

La inyección SQL ( inglés  SQL injection /SQLi ) es una de las formas más comunes de piratear sitios y programas que funcionan con bases de datos , basándose en la introducción de código SQL arbitrario en una consulta .

La inyección de SQL, según el tipo de DBMS utilizado y las condiciones de la inyección, puede permitir que un atacante ejecute una consulta arbitraria a la base de datos ( por ejemplo, lea el contenido de cualquier tabla , elimine, modifique o agregue datos ), obtenga la capacidad para leer y/o escribir archivos locales y ejecutar comandos arbitrarios en el servidor atacado.

Un ataque de tipo inyección SQL puede ser posible debido al procesamiento incorrecto de los datos de entrada utilizados en las consultas SQL.

Un desarrollador de aplicaciones de bases de datos debe estar al tanto de tales vulnerabilidades y tomar medidas para contrarrestar la inyección de SQL.

Tipos de ataques como inyección SQL

Hay tres clases principales de ataques basados ​​en inyección SQL:

Principio de ataque de inyección SQL

Digamos que el software del servidor , habiendo recibido el parámetro de entrada id, lo usa para crear una consulta SQL. Considere la siguiente secuencia de comandos PHP :

$id = $_SOLICITUD [ 'id' ]; $res = mysqli_query ( "SELECCIONE * DESDE noticias DONDE id_noticias = " . $id );

Si se pasa al servidor un parámetro de identificación igual a 5 (por ejemplo: http://example.org/script.php?id=5 ), se ejecutará la siguiente consulta SQL :

SELECCIONE * DESDE noticias DONDE id_noticias = 5

Pero si un atacante pasa la cadena -1 O 1=1 como parámetro de identificación (por ejemplo, así: http://example.org/script.php?id=-1+OR+1=1 ), entonces el se ejecutará la solicitud:

SELECCIONE * DESDE noticias DONDE id_noticias = - 1 O 1 = 1

Por lo tanto, cambiar los parámetros de entrada agregándoles construcciones del lenguaje SQL provoca un cambio en la lógica de ejecución de la consulta SQL (en este ejemplo, en lugar de noticias con un identificador dado, se seleccionarán todas las noticias en la base de datos, ya que la expresión 1=1 siempre es cierto: los cálculos se realizan utilizando el contorno más corto del diagrama ).

Inyección en parámetros de cadena

Supongamos que el software del servidor, habiendo recibido una solicitud para buscar datos en las noticias con el parámetro search_text, lo usa en la siguiente consulta SQL (aquí los parámetros se escapan entre comillas):

$buscar_texto = $_SOLICITUD [ 'buscar_texto' ]; $res = mysqli_query ( "SELECCIONE id_noticias, fecha_noticias, título_noticias, texto_noticias, id_noticias_autor DESDE noticias DONDE título_noticias LIKE('% $texto_búsqueda %')" );

Al hacer una consulta como http://example.org/script.php?search_text=Test , obtenemos que se ejecute la siguiente consulta SQL:

SELECCIONE id_news , news_date , news_caption , news_text , news_id_author DESDE news DONDE news_caption LIKE ( '%Test%' )

Pero al incorporar un carácter de comillas (que se usa en la consulta) en el parámetro search_text, podemos cambiar drásticamente el comportamiento de la consulta SQL. Por ejemplo, al pasar el valor ' )+and+(news_id_author='1 ) como el parámetro search_text , llamaremos a la consulta para que se ejecute:

SELECCIONE id_news , news_date , news_caption , news_text , news_id_author DESDE noticias DONDE news_caption LIKE ( '%' ) y ( news_id_author = '1%' )

Usando UNION

El lenguaje SQL le permite combinar los resultados de múltiples consultas usando el operador UNION . Esto proporciona a un atacante la oportunidad de obtener acceso no autorizado a los datos.

Consideremos el script de visualización de noticias ( el identificador de las noticias que se mostrarán se pasa en el parámetro id ):

$res = mysqli_query ( "SELECT id_noticias, encabezado, cuerpo, autor DESDE noticias DONDE id_noticias = " . $_REQUEST [ 'id' ]);

Si un atacante pasa -1 UNION SELECT 4 nombre de usuario, contraseña, 1 DE admin como el parámetro id , esto hará que se ejecute la consulta SQL

SELECCIONE id_noticias , encabezado , cuerpo , autor DESDE noticias DONDE id_noticias = - 1 UNION SELECCIONE 1 , nombre de usuario , contraseña , 1 DESDE administrador

Dado que las noticias con el identificador -1 ciertamente no existen, no se seleccionarán registros de la tabla de noticias, pero el resultado incluirá registros que se seleccionaron ilegalmente de la tabla de administración como resultado de la inyección SQL.

Usando UNION + group_concat()

En algunos casos, un hacker puede atacar pero no puede ver más de una columna. En el caso de MySQL , un atacante puede usar la función:

group_concat ( columna , símbolo , columna )

que combina varias columnas en una sola. Por ejemplo, para el ejemplo anterior, la llamada a la función sería:

- 1 UNION SELECT group_concat ( nombre de usuario , 0 x3a , contraseña ) DESDE admin

Escape de cola de consulta

A menudo, la consulta SQL afectada por esta vulnerabilidad tiene una estructura que dificulta o imposibilita el uso de union. Por ejemplo guión:

$res = mysqli_query ( "SELECCIONE el autor DESDE noticias DONDE id=" . $_REQUEST [ 'id' ] . " Y autor LIKE ('a%')" );

muestra el nombre del autor de la noticia por el identificador de identificación pasado solo si el nombre comienza con la letra a, y la inyección de código con el operador UNION es difícil.

En tales casos, los atacantes utilizan el método de escapar de parte de la solicitud utilizando caracteres de comentario ( /* o -- según el tipo de DBMS).

En este ejemplo, un atacante puede pasar el parámetro id con el valor -1 UNION SELECT password FROM admin/* al script , ejecutando así la consulta

SELECCIONE autor DE noticias DONDE id =- 1 UNION SELECCIONE contraseña DE admin /* Y autor LIKE ('a%')

en qué parte de la consulta ( AND author LIKE ('a%') ) se marca como comentario y no afecta la ejecución.

Dividir una consulta SQL

El símbolo ;se utiliza para separar comandos en el lenguaje SQL ; ( punto y coma ) al incorporar este carácter en una consulta, un atacante puede ejecutar varios comandos en una sola consulta; sin embargo, no todos los dialectos de SQL admiten esta capacidad.

Por ejemplo, si en los parámetros del script

$id = $_SOLICITUD [ 'id' ]; $res = mysqli_query ( "SELECCIONE * DESDE noticias DONDE id_noticias = $id " );

el atacante pasa una construcción que contiene un punto y coma, por ejemplo 12;INSERT INTO admin (nombre de usuario, contraseña) VALUES ('HaCkEr', 'foo'); entonces se ejecutarán 2 comandos en una consulta

SELECCIONE * DESDE noticias DONDE id_noticias = 12 ; INSERTAR EN admin ( nombre de usuario , contraseña ) VALORES ( 'HaCkEr' , 'foo' );

y se agregará un registro de HaCkEr no autorizado a la tabla de administración.

Técnicas de ataque de inyección SQL

Encontrar scripts vulnerables a ataques

En esta etapa, el atacante examina el comportamiento de los scripts del servidor al manipular los parámetros de entrada para detectar su comportamiento anómalo. La manipulación se produce con todos los parámetros posibles:

  • Datos pasados ​​a través de los métodos POST y GET
  • Valores de [cookie HTTP]
  • HTTP_REFERER (para secuencias de comandos)
  • AUTH_USER y AUTH_PASSWORD (al usar la autenticación)

Como regla general, la manipulación se reduce a sustituir una comilla simple (rara vez doble o inversa) en los parámetros del carácter.

El comportamiento anómalo es cualquier comportamiento en el que las páginas recuperadas antes y después de la sustitución de comillas son diferentes (y no muestran la página de formato de parámetro no válido).

Los ejemplos más comunes de comportamiento anómalo son:

  • se muestran varios mensajes de error;
  • al solicitar datos (por ejemplo, noticias o una lista de productos), no se muestran en absoluto los datos solicitados, aunque se muestra la página

etc. Debe tenerse en cuenta que hay casos en que los mensajes de error, debido a las características específicas del marcado de la página, no son visibles en el navegador, aunque están presentes en su código HTML.

Diseño Comentando el resto de la línea Obtener versión Concatenación de cadenas
mysql -- ..., /* ...o# ... version() concat (string1, string2)
msql -- ... @@version string1 + string2
Oráculo -- ...o/* ... select banner
from v$version
string1 || string2
oconcat (string1, string2)
MS Access Inyectando un byte NULL en una solicitud:%00...
postgresql -- ... SELECT version() string1 || string2,CONCAT('a','b')
Sybase -- ... @@version string1 + string2
ibmdb2 -- ... select versionnumber from sysibm.sysversions string1 || string2ostring1 concat string2
Ingres -- ... dbmsinfo('_version') string1 || string2

Protección contra ataques como inyección SQL

Para protegerse contra este tipo de ataque, es necesario filtrar cuidadosamente los parámetros de entrada, cuyos valores se utilizarán para construir la consulta SQL.

Filtrado de parámetros de cadena

Supongamos que el código que genera la solicitud (en el lenguaje de programación Pascal ) se ve así:

sentencia := 'SELECT * FROM usuarios WHERE nombre = "' + nombre de usuario + '";' ;

Para hacer la inyección de código (cerrar una cadena que comienza con una comilla con otra comilla antes de que termine con la comilla de cierre actual para dividir la consulta en dos partes) era imposible, para algunos DBMS , incluido MySQL , se requiere citar todos los parámetros de cadena . En el parámetro en sí, reemplace las comillas con \", el apóstrofe con \', la barra invertida con \\ (esto se llama " caracteres especiales de escape "). Esto se puede hacer con el siguiente código:

declaración : = 'SELECCIONAR * DE usuarios DONDE nombre = ' + QuoteParam ( nombre de usuario ) + ';' ; función QuoteParam ( s : cadena ) : cadena ; { en la entrada - una cadena; la salida es una cadena entre comillas y con caracteres especiales reemplazados } var i : integer ; destino : cadena _ begin Dest := '"' ; for i := 1 to length ( s ) do case s [ i ] of ' '' ' : Dest := Dest + '\ '' ' ; '"' : Dest := Dest + '\"' ; '\' : Dest := Dest + '\\' ; else Dest := Dest + s [ i ] ; end ; QuoteParam := Dest + '"' ; fin ;

Para PHP, el filtrado puede ser así:

$consulta = "SELECCIONAR * DESDE usuarios DONDE usuario='" . mysqli_real_escape_string ( $usuario ) . "';" ;

Filtrado de parámetros enteros

Hagamos otra consulta:

declaración := 'SELECT * FROM usuarios DONDE id = ' + id + ';' ;

En este caso, el campo idtiene un tipo numérico y, en la mayoría de los casos, no se cita. Por lo tanto, "entrecomillar" y reemplazar caracteres especiales con secuencias de escape no funciona. En este caso, la verificación de tipos ayuda; si la variable idno es un número, la consulta no debería ejecutarse en absoluto.

Por ejemplo, en Delphi , el siguiente código ayuda a contrarrestar tales inyecciones:

si TryStrToInt ( id , id_int ) entonces declaración := Formato ( 'SELECT * FROM usuarios DONDE id =%0:d;' , [ id_int ]) ;

Para PHP, este método se vería así:

$consulta = 'SELECT * FROM usuarios DONDE id = ' . ( int ) $id ;

Truncamiento de parámetros de entrada

Para realizar cambios en la lógica de ejecución de una consulta SQL, se requiere la inyección de cadenas suficientemente largas. Por lo tanto, la longitud mínima de la cadena incrustada en los ejemplos anteriores es de 8 caracteres (" 1 OR 1=1 "). Si la longitud máxima de un valor de parámetro válido es pequeña, uno de los métodos de protección puede ser el truncamiento máximo de los valores de los parámetros de entrada.

Por ejemplo, si se sabe que el campo idde los ejemplos anteriores puede tomar valores de no más de 9999, puede "cortar los caracteres adicionales", dejando no más de cuatro:

instrucción : = 'SELECCIONAR * DE usuarios DONDE id = ' + LeftStr ( id , 4 ) + ';' ;

Uso de consultas parametrizadas

Muchos servidores de bases de datos admiten la capacidad de enviar consultas parametrizadas (sentencias preparadas). En este caso, los parámetros de origen externo se envían al servidor por separado de la solicitud en sí, o la biblioteca del cliente los escapa automáticamente. Para ello se utilizan

  • en Delphi  - propiedad TQuery.Params;

Por ejemplo

var sql , parámetro : cadena begin sql := 'select :text as value from dual' ; parámetro := 'alfa' ; Consulta1 . Sql . Texto : = sql Consulta1 . ParamByName ( 'texto' ) . AsString := parámetro ; Consulta1 . abierto ; ShowMessage ( Consulta1 [ 'valor' ]) ; fin ;
  • en Perl  - a través de DBI::quoteo DBI::prepare;
  • en Java  , a través de la clase PreparedStatement;
  • en C#  - propiedad SqlCommand.Parameters;
  • en PHP  - MySQLi (cuando se trabaja con MySQL ), PDO.

Véase también

Enlaces