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.
Hay tres clases principales de ataques basados en 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 = 5Pero 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 = 1Por 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 ).
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%' )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 administradorDado 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.
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 adminA 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.
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.
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:
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:
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 |
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.
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 ) . "';" ;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 ;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 ) + ';' ;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
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 ;