Seguridad de Bases de Datos
PHP Manual

Inyección de SQL

Muchos desarrolladores web son desprevendios de cómo las consultas SQL pueden ser manipuladas, y asumen que una consulta SQL es un comando confiable. Esto significa que las consultas SQL están expuestas a que sean malversadas en controles de acceso, y por lo tanto, sobrepasar las revisiones de autenticación y autorización estándar, y que algunas veces las consultas SQL aún podrían permitir el acceso de comandos a nivel de sistema operativo del ordenador.

Comandos directos de Inyección SQL es una técnica donde un atacante crea o altera comandos SQL existentes para exponer datos ocultos, sobreponerse a los que son importantes, o peor aún, ejecutar comandos peligrosos a nivel de sistema en el equipo donde se encuentra la base de datos. Esto se logra a través de la aplicación, tomando la entrada del usuario y combinándola con parámetros estáticos para elaborar una consuta SQL. Los siguientes ejemplos están basados en historias reales, desafortunadamente.

Debido a la falta de validación en la entrada de datos y conectándose a la base de datos con privilegios de super usuario o de alguien con privilegios que puede crear usuarios, el atacante podría crear un super usuario en su base de datos.

Ejemplo #1 Dividiendo el conjunto de resultados en páginas ... y haciendo super usuarios (PostgreSQL)

<?php

$offset 
$argv[0]; // Cuidado, no hay validación en la entrada de datos!
$query  "SELECT id, name FROM products ORDER BY name LIMIT 20 OFFSET $offset;";
$result pg_query($conn$query);

?>
Los usuarios normales dan clic en los enlaces 'siguiente' o 'atras' donde $offset está codificado en la URL. El script espera que el $offset entrante sea un número décimal. Sin embargo, qué pasa si alguien intenta irrumpir añadiendo una función urlencode() al formulario de la siguiente URL
0;
insert into pg_shadow(usename,usesysid,usesuper,usecatupd,passwd)
    select 'crack', usesysid, 't','t','crack'
    from pg_shadow where usename='postgres';
--
Si esto sucedió, entonces el script podría presentarle un acceso de super usuario al atacante. Nótese que 0; es para proveer un offset válido a la consulta original y para finalizarla.

Nota:

Esta es una técnica común para forzar al analizador SQL a que ignore el resto de la consulta escrita por el desarrollador con dos guiónes: -- los cuales representan un comentario en SQL.

Una forma factible de obtener contraseñas es burlar las páginas de búsqueda de resultados. Lo único que el atacante necesita hacer es ver si hay variables que hayan sido enviadas y sean usadas en declaraciones SQL las cuales no sean manejadas apropiadamente. Esos filtros pueden ser puestos comunmente en un formulario anterior para personalizar las cláusulas WHERE, ORDER BY, LIMIT y OFFSET en las declaraciones SELECT. Si su base de datos soporta el constructor UNION, el atacante podría intentar añadir una consulta enetera a la consulta original para listar contraseñas de una tabla arbitraria. Utilizar campos de contraseña encriptadoslds es fuertemente recomendado.

Ejemplo #2 Listando nuestros artículos ... y algunas contraseñas (de cualquier servidor de base de datos)

<?php

$query  
"SELECT id, name, inserted, size FROM products
           WHERE size = '
$size'";
$result odbc_exec($conn$query);

?>
La parte estática de la consulta puede ser combinada con otra declaración SELECT la cual revela todas las contraseñas:
'
union select '1', concat(uname||'-'||passwd) as name, '1971-01-01', '0' from usertable;
--
Si esta consulta (ejecutándose con ' y --) fuera asignada a una de las variables utilizadas en $query, la consulta reaccionará bestialmente.

Las consultas de actualización de SQL, también son susceptibles a ataques. Estas consultas también son amenazadas por acortamiento y adición en una consulta completamente nueva a esta. Sin embargo el atacante podría manipularla con la cláusula SET. En este caso, algunos esquemas de información deben ser procesados para manipular la consulta exitosamente. Este puede adquirirse examinando la forma de nombres de las variables, o simplemente forzarlo con un ataque de fuerza bruta. No hay muchas convenciones de nombres para campos que almacenan contraseñas o nombres de usuarios.

Ejemplo #3 Desde re-establecer una contraseña ... hasta ganar más privilegios (en cualquier servidor de bases de datos)

<?php
$query 
"UPDATE usertable SET pwd='$pwd' WHERE uid='$uid';";
?>
Pero un usuario malicioso podría enviar el valor ' or uid like'%admin% a $uid para cambiar la contraseña del administrador, o simplemente cambiar $pwd a hehehe', trusted=100, admin='yes para obtener más privilegios. Entonces, la consulta sería cambiada:
<?php

// $uid: ' or uid like '%admin%
$query "UPDATE usertable SET pwd='...' WHERE uid='' or uid like '%admin%';";

// $pwd: hehehe', trusted=100, admin='yes
$query "UPDATE usertable SET pwd='hehehe', trusted=100, admin='yes' WHERE
...;"
;

?>

Un ejemplo horrible de cómo pueden ser accedidos los comandos a nivel de sistema operativo en algunos hospedadores de bases de datos.

Ejemplo #4 Atacando el sistema operativo que hospeda la base de datos (Servidor MSSQL)

<?php

$query  
"SELECT * FROM products WHERE id LIKE '%$prod%'";
$result mssql_query($query);

?>
Si un atacante envía el valor a%' exec master..xp_cmdshell 'net user test testpass /ADD' -- hacia $prod, la consulta $query será:
<?php

$query  
"SELECT * FROM products
           WHERE id LIKE '%a%'
           exec master..xp_cmdshell 'net user test testpass /ADD' --%'"
;
$result mssql_query($query);

?>
El servidor MSSQL ejecuta la sentencia SQL en el lote que incluye un comando para añadir un usuario nuevo a la base de datos de cuentas locales. Si esta aplicación estuviera ejecutándose como sa, y el servicio MSSQLSERVER se está ejecutando con los privilegios suficientes, el atacante ahora podría tener una cuenta con la cual tendría acceso a esta máquina.

Nota:

Algunos de los ejemplos de citados arriba estan vinculados a un servidor de base de datos específico. Esto no significa que un ataque similar sea imposible en contra de otros productos. Su servidor de base de datos podría ser vulnerable de forma similar en otra manera.

Un ejemplo comprobado de los problemas con respecto a las inyecciones de SQL
Imagen cortesía de » xkcd

Técnicas de evitación

Pese a que pueda parecer obvio que un atacante debe tener al menos algún conocimiento de arquitecturas de bases de datos para poder realizar un ataque con éxito, el obtener esta información suele ser muy sencillo. Por ejemplo, cuando la base de datos forma parte de un paquete de software libre, o disponible públicamente, con una instalación predefinida, esta información se encuentra completamente libre y disponible. Esta información puede haber sido divulgada en proyectos de código cerrado - incluso si está codificad, ofuscada o compilada - incluso por el propio código mediante mensajes de error. Otros métodos incluyen el uso de nombres de tablas y columnas frecuentes. Por ejemplo, un formulario de inicio de sesión que utiliza una tabla 'users' con los nombres de columna 'id', 'username', y 'password'.

Esos ataques están principalmente basados en explotar el código que no ha sido escrito teniendo en mente la seguridad. Nunca confíes en ningún tipo de entrada, especialmente la que viene del lado del cliente, aún cuando esta venga de una caja de selección, un campo oculto o una cookie. El primer ejemplo muestra que una inofensiva consulta puede causar desastres.

Junto a esto, usted se beneficia de tener un registro de las consultas ya sea dentro de su script o de la base de datos en si misma, si es que esta soporta el registro. Obviamente, llevar un registro no le previene cualquier intento de daño, pero éste puede ser útil para hacer una retro revisión de cual aplicación ha sido intervenida. El registro no es útil por sí mismo, pero lo es debido a la información que contiene. Más detalles generalmente es mejor que los pocos.


Seguridad de Bases de Datos
PHP Manual