Inyección de SQL
Muchos desarrolladores web no son conscientes de cómo pueden manipularse las consultas SQL, y asumen que una consulta SQL es un comando confiable. Esto representa que las consultas SQL pueden burlar los controles de acceso, y de este modo evitar los chequeos estándares de autenticación y autorización, y a veces las consultas SQL pueden incluso permitir acceso a comandos al nivel del sistema operativo de la máquina huésped.
La Inyección Directa de Comandos SQL es una técnica en la cual un atacante crea o altera comandos SQL existentes para exponer datos escondidos, o sobrescribir datos crÃticos, o incluso ejecutar comandos del sistema peligrosos en la máquina en donde se encuentra la base de datos. Esto se consigue cuando la aplicación toma información de entrada del usuario y la combina con parámetros estáticos para construir una consulta SQL. Los siguientes ejemplos, desafortunadamente, están basados en historias reales.
Debido a la falta de validación de la información de entrada y el establecimiento de conexiones con la base de datos desde un super-usuario o aquel que puede crear usuarios, el atacante podrÃa crear un super-usuario en su base de datos.
Example#1 Paginación del conjunto de resultados ... y creación de super-usuarios (PostgreSQL)
<?php
$offset = $argv[0]; // atención, ¡no se valida la entrada!
$consulta = "SELECT id, nombre FROM productos ORDER BY nombre LIMIT 20 " .
"OFFSET $offset;";
$resultado = pg_query($conexion, $consulta);
?>
0; insert into pg_shadow(usename,usesysid,usesuper,usecatupd,passwd) select 'crack', usesysid, 't','t','crack' from pg_shadow where usename='postgres'; --
Note: Es una técnica común obligar al analizador sintáctico de SQL a que ignore el resto de la consulta escrita por el desarrollador mediante --, el cual es el signo de comentarios en SQL.
Una forma viable de adquirir contraseñas es jugar con las páginas de resultados de búsquedas. Lo único que necesita el atacante es ver si existen variables enviadas por el usuario que sean usadas en sentencias SQL, y que no sean tratadas apropiadamente. Estos filtros pueden ubicarse por lo general previos a cláusulas WHERE, ORDER BY, LIMIT y OFFSET en sentencias SELECT para personalizar la instrucción. Si su base de datos soporta la construcción UNION, el atacante puede intentar añadir una consulta completa a la consulta original para generar una lista de contraseñas desde una tabla cualquiera. El uso de campos encriptados de contraseñas es altamente recomendable.
Example#2 Listado de artÃculos ... y algunas contraseñas (en cualquier base de datos)
<?php
$consulta = "SELECT id, nombre, insertado, tam FROM productos
WHERE tam = '$tam'
ORDER BY $orden LIMIT $limite, $offset;";
$resultado = odbc_exec($conexion, $consulta);
?>
' union select '1', concat(uname||'-'||passwd) as name, '1971-01-01', '0' from usertable; --
Las sentencias UPDATE de SQL son también susceptibles a ataque. Estas consultas también se encuentran amenazadas por un posible acotamiento y adición de una consulta completamente nueva. Pero en este caso el atacante puede amañar la información de una cláusula SET. En este caso se requiere contar con cierta información sobre el esquema de la base de datos para poder manipular la consulta satisfactoriamente. Esta información puede ser adquirida mediante el estudio de los nombres de variables de los formularios, o simplemente por fuerza bruta. No existen demasiadas convenciones para nombrar campos de contraseñas o nombres de usuario.
Example#3 De restablecer una contraseña ... a adquirir más privilegios (con cualquier servidor de base de datos)
<?php
$consulta = "UPDATE usertable SET pwd='$pwd' WHERE uid='$uid';";
?>
<?php
// $uid == ' or uid like'%admin%'; --
$consulta = "UPDATE usertable SET pwd='...' WHERE uid='' or uid like '%admin%'; --";
// $pwd == "hehehe', admin='yes', trusted=100 "
$consulta = "UPDATE usertable SET pwd='hehehe', admin='yes', trusted=100 WHERE ...;"
?>
Un horrible ejemplo de cómo puede accederse a comandos del nivel del sistema operativo en algunas máquinas anfitrionas de bases de datos.
Example#4 Ataque al sistema operativo de la máquina anfitriona de la base de datos (MSSQL Server)
<?php
$consulta = "SELECT * FROM productos WHERE id LIKE '%$prod%'";
$resultado = mssql_query($consulta);
?>
<?php
$consulta = "SELECT * FROM productos
WHERE id LIKE '%a%'
exec master..xp_cmdshell 'net user test testpass /ADD'--";
$resultado = mssql_query($consulta);
?>
Note: Algunos de los ejemplos anteriores están atados a un servidor de base de datos especÃfico. Esto no quiere decir que un ataque similar sea imposible con otros productos. Su base de datos puede ser vulnerable de forma semejante, en alguna otra manera.
Técnicas de protección
Usted puede argumentar con justa razón que el atacante debe poseer cierta cantidad de información sobre el esquema de la base de datos en la mayorÃa de ejemplos que hemos visto. Tiene razón, pero usted nunca sabe cuándo y cómo puede filtrarse esta información, y si ocurre, su base de datos estará expuesta. Si está usando un paquete de gestión de bases de datos de código abierto, o cuyo código fuente está disponible públicamente, el cual puede pertenecer a algún sistema de administración de contenido o foro, los intrusos pueden producir fácilmente una copia de un trozo de su código. También puede ser un riesgo de seguridad si es un segmento de código pobremente diseñado.
Estos ataques se basan principalmente en la explotación del código que no ha sido escrito pensando en la seguridad. Nunca confÃe en ningún tipo de información de entrada, especialmente aquella que proviene del lado del cliente, aun si lo hace desde una caja de selección, un campo de entrada hidden o una cookie. El primer ejemplo le muestra que una consulta asà de descuidada puede causar desastres.
- Nunca se conecte a la base de datos como un super-usuario o como el dueño de la base de datos. Use siempre usuarios personalizados con privilegios muy limitados.
- Revise si la entrada recibida es del tipo apropiado. PHP posee un amplio rango de funciones de validación de datos, desde los más simples encontrados en Funciones sobre variables y en Funciones de tipo de caracter (p. ej. is_numeric(), ctype_digit() respectivamente) hasta el soporte para Expresiones Regulares compatibles con Perl.
-
Si la aplicación espera alguna entrada numérica, considere la verificación de información con is_numeric(), o modifique silenciosamente su tipo usando settype(), o utilice su representación numérica, dada por sprintf().
Example#5 Una forma más segura de generar una consulta para paginado
<?php
settype($offset, 'integer');
$consulta = "SELECT id, nombre FROM productos ORDER BY nombre " .
"LIMIT 20 OFFSET $offset;";
// note el simbolo %d en la cadena de formato, usar %s no tendrÃa sentido
$consulta = sprintf("SELECT id, nombre FROM productos ORDER BY nombre" .
"LIMIT 20 OFFSET %d;", $offset);
?> - Ubique cada valor no-numérico que entrega el usuario y que sea pasado a la base de datos entre comillas con la función de escape de cadenas especÃfica a su base de datos (p.ej. mysql_escape_string(), sql_escape_string(), etc.). Si no hay disponible un mecanismo de escape de cadenas especÃfico a la BD, las funciones addslashes() y str_replace() pueden ser útiles (dependiendo del tipo de base de datos). Vea el primer ejemplo. Como se ve allÃ, agregar comillas a la parte estática de la consulta no es suficiente, haciéndola fácilmente manipulable.
- No imprima ninguna información especÃfica sobre la base de datos, especialmente sobre su esquema, ya sea por razones justas o por equivocaciones. Vea también Reporte de Errores y Gestión de Errores y Funciones de Registro.
- Puede usar procedimientos almacenados y cursores previamente definidos para abstraer el acceso a las bases de datos, de modo que los usuarios no tengan acceso directo a las tablas o vistas, aunque esta solución tiene otros impactos.
Además de estas acciones, usted puede beneficiarse del registro explÃcito de las consultas realizadas, ya sea desde su script o por la base de datos misma, si ésta soporta la gestión de registros. Por supuesto, el registro de acciones no puede prevenir cualquier intento peligroso, pero puede ser útil para rastrear cuáles aplicaciones han sido usadas para violar la seguridad. El registro en sà no es útil; lo es la información que contiene. Por lo general, es mejor contar con más detalles que con menos.