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.
Ejemplo 27-2. Paginación del conjunto de resultados ... y creación de super-usuarios (PostgreSQL)
<?php

$offset   
$argv[0]; // atencion, no se valida la entrada!$consulta "SELECT id, nombre FROM productos ORDER BY nombre LIMIT 20 " .
            
"OFFSET $offset;";
$resultado pg_query($conexion$consulta);
?>
Los usuarios normales pulsan sobre los enlaces 'siguiente' y 'anterior', en donde el desplazamiento ($offset) se encuentra codificado en la URL. El script espera que el valor entrante $offset sea un número decimal. Sin embargo, qué sucede si alguien intenta un ataque añadiendo una forma codificada (urlencode()) de lo siguiente en la 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 ocurriera, entonces el script le presentaría un acceso de superusuario al atacante. Note que 0; es usado para ofrecer un desplazamiento válido a la consulta original y finalizarla.

Nota: 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ónUNION, 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.
Ejemplo 27-3. 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);
?>
La parte estática de la consulta puede combinarse con otra sentencia 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 (la cual juega con ' y --) fuera asignada a una de las variables usadas en $consulta, la bestia de la consulta habrá despertado.


Las sentencias UPDATE de SQL son también susceptibles a ataque. Éstas 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.
Ejemplo 27-4. 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';";?>
Pero un usuario malicioso envía el valor ' or uid like'%admin%'; -- como $uid para cambiar la contraseña del administrador, o simplemente establece $pwd a "hehehe', admin='yes', trusted=100 " (con un espacio al inicio) para adquirir más privilegios. En tal caso, la consulta sería manipulada:



<?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.
Ejemplo 27-5. 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);
?>
Si el atacante envía el valor a%' exec master..xp_cmdshell 'net user test testpass /ADD' -- a $prod, entones la $consulta será:



<?php

$consulta  
"SELECT * FROM productos
                    WHERE id LIKE '%a%'
                    exec master..xp_cmdshell 'net user test testpass /ADD'--"
;$resultado mssql_query($consulta);
?>

MSSQL Server ejecuta sentencias SQL en el lote, incluyendo un comando para agregar un nuevo usuario a la base de datos de cuentas locales. Si esta aplicación estuviera corriendo como sa y el servicio MSSQLSERVER está corriendo con los privilegios suficientes, el atacante tendría ahora una cuenta con la que puede acceder a esta máquina.

Nota: 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().
    Ejemplo 27-6. 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 tendria 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.

No response to “Inyección de SQL”