Entradas etiquetadas ‘sql’
Inyección SQL: qué es y sus peligros
Hace tiempo hablé brevemente sobre la inyección SQL y cómo evitarla en ciertos casos concretos con PHP. En este post intentaré profundizar un poco más en este tema y dejar para uno posterior soluciones al problema.
Qué es la inyección SQL
La inyección SQL es una técnica usada para aprovecharse de las entradas que recibe una aplicación, no validadas y utilizadas posteriormente en la construcción de sentencias SQL. Un error muy común a la hora de desarrollar una aplicación web es usar los parámetros proporcionados por el usuario directamente en las consultas, lo que aprovecha el atacante para introducir las modificaciones que necesite para su malicioso cometido. Así, se podría ejecutar cualquier tipo de sentencia SQL en la base de datos valiéndose de estos agujeros de la aplicación o página web.
Como su propio nombre indica, la inyección SQL se da en dicho lenguaje y sobre cualquier plataforma que lo utilice para trabajar contra una BD.
Ejemplo rápido y sencillo
Si tenemos un formulario de búsqueda de usuarios que utiliza la siguiente consulta para conseguir los resultados:
String consulta = "SELECT * FROM usuarios WHERE nombre = '" + texto + "'";
el usuario puede introducir, por ejemplo, pepe, resultando una consulta correcta:
SELECT * FROM usuarios WHERE nombre = 'pepe';
Pero, ¿qué ocurre si introduce algo como pepe' OR 'a'='a?
SELECT * FROM usuarios WHERE nombre = 'pepe' OR 'a'='a';
Y esta consulta, como vemos, haría que la cláusula del where se cumpla siempre devolviéndonos todos los registros de la tabla.
Peligros principales
- Confidencialidad: las bases de datos contienen datos importantes y privados, la pérdida de confidencialidad es un problema frecuente en las vulnerabilidades de inyección SQL.
- Autenticación: si se utilizan sentencias SQL mal construidas para la comprobación de usuario y contraseña, se podría utilizar este agujero de seguridad para loguearnos con cualquier identidad en un sistema.
- Autorización: como cualquier información almacenada en la base de datos, los permisos y privilegios de un sistema pueden verse afectados por este tipo de ataques.
- Integridad: las posibles modificaciones y borrados que puede producir un ataque de inyección SQL conllevan un peligro enorme para la integridad de los datos.
Por desgracia, este problema es fácilmente detectable y explotable, por lo que hay que tener un cuidado extremo para evitar a toda costa la inyección SQL. En el próximo post veremos algunas soluciones sencillas y eficaces para varios lenguajes y plataformas.
Ignorar tildes y mayúsculas en las consultas de Oracle
Oracle, al contrario que MySQL, sí diferencia entre mayúsculas y minúsculas en las búsquedas. Esto, unido a la tradicional problemática con las tildes y demás símbolos extraños, hace que debamos tomar medidas en nuestras consultas para conseguir los resultados deseados.
Así, si nuestra búsqueda era:
[sql]
SELECT *
FROM tabla
WHERE campo LIKE ‘%texto%’;
[/sql]
si queremos que nos devuelva las coincidencias ignorando tildes y mayúsculas, la transformaríamos en:
[sql]
SELECT *
FROM tabla
WHERE UPPER(translate(campo, ‘áéíóúÁÉÍÓÚ’, ‘aeiouAEIOU’)) LIKE UPPER(translate(‘%texto’, ‘áéíóúÁÉÍÓÚ’, ‘aeiouAEIOU’));
[/sql]
La función UPPER convierte todos los caracteres a mayúsculas en ambos lugares (base de datos y texto a buscar), de forma que se ignoren las diferencias por estar en mayúsculas o minúsculas.
Con translate transformamos una serie de caracteres en otros, concretamente en el caso anterior estaríamos eliminando las tildes.
No es un método sencillo, quizá haya alguna forma de configurar Oracle para evitar realizar estas operaciones, pero funciona.
Valor de comienzo para campos auto_increment
Los campos tipo auto_increment, como sabemos, son generados automáticamente por la base de datos incrementando el índice en una cantidad determinada en cada inserción. Son especialmente útiles cuando no tenemos una clave primaria a priori; necesitamos crear una artificial para distinguir los registros y delegamos en la base de datos su gestión.
En ocasiones puede resultarnos interesante establecer el valor de inicio para este tipo de campo. Por ejemplo, si tenemos una web en la que se realizan pedidos podemos dar impresión de un gran volumen de cara al cliente identificando su compra con el número 1020 en lugar de con el 20.
Para modificar el valor de comienzo de un campo auto_incremente en MySQL simplemente debemos añadir al final de la sentencia de creación de la tabla lo siguiente:
[sql]
CREATE TABLE tabla (
…
) auto_increment = 1000
[/sql]
Si la tabla ya existe, podemos indicar cuál será el siguiente valor a generar de una forma similar a la anterior:
[sql]
ALTER TABLE tabla SET auto_increment = 1000;
[/sql]
Cómo almacenar imágenes en MySQL
Poco a poco y según el tiempo me lo permita intentaré ir vaciando la larga lista de borradores que tengo pendiente. Para empezar veremos cómo almacenar imágenes directamente en MySQL, un método que como habíamos visto tenía sus ventajas e inconvenientes.
Comenzamos pues con el código correspondiente al formulario desde el que enviaremos el archivo (podemos tenerlo ya en el servidor, lo cual sería todavía más sencillo). Para permitir el envío de ficheros desde un formulario:
[html]
Archivo:
[/html]
Los datos serán almacenados en una tabla que tendrá un campo de tipo longblob (para datos binarios):
[sql]
CREATE TABLE tabla (
`id` int(11) unsigned NOT NULL auto_increment,
`archivo` varchar(255) NOT NULL default ”,
`mime` varchar(255) NOT NULL default ”,
`contenido` longblob NOT NULL,
PRIMARY KEY ( `id` )
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
[/sql]
El resto de los campos servirá para almacenar información sobre el archivo (archivo para el nombre y mime para el tipo).
Finalmente, el archivo php que recuperará el fichero y lo almacenará en la base de datos:
[php]
$archivo = $_FILES['archivo']['name'];
$mime = $_FILES['archivo']['type'];
$tamano = $_FILES['archivo']['size'];
// guardamos en $tmp el contenido del fichero
$tmp = addslashes( file_get_contents( $_FILES['archivo']['tmp_name'] ) );
$consulta = “INSERT INTO tabla
(archivo, mime, contenido)
VALUES (‘$archivo’, ‘$mime’, ‘$tmp’);”;
mysql_query($consulta);
[/php]
Un script sencillito y que nos puede ahorrar mucho trabajo a la hora de gestionar los archivos, siendo mucho más sencillo hacerlo sobre una base de datos que sobre el sistema de ficheros.
Seguridad en PHP: inyección SQL
Uno de los aspectos más importantes de una aplicación web es su seguridad, debido sobre todo a la gran capacidad de interacción que tiene con los usuarios. Así, el programador debe tener en cuenta este aspecto y cuidar todas las entradas que procedan o puedan ser manipuladas por los visitantes.
La inyección SQL consiste en inyectar, valga la redundancia, código SQL dentro de una variable con el propósito de modificar la consulta que se ejecutará, intentando obtener o destruir datos. Veámoslo con un pequeño ejemplo:
[php]
$id = $_GET['id'];
$posts = mysql_query(“SELECT * FROM posts WHERE id = $id”);
[/php]
Este código recibe vía url una variable a partir de la cual recuperar unos valores en la base de datos. Sin embargo este código es vulnerable a cosas tan sencillas como que el usuario escriba en su barra de direcciones algo como script.php?id=12 UNION SELECT * FROM users.
La consulta real que se ejecutaría en este caso pasaría a ser:
[sql]
SELECT * FROM posts WHERE id = 12 UNION SELECT * FROM users
[/sql]
algo que por supuesto no querremos que suceda por la cantidad de datos que pueden quedar al descubierto. Y todavía podría ser peor si el código inyectado fuese alguna modificación o borrado.
Para solucionarlo debemos chequear al máximo cualquier entrada que proceda o pueda ser modificada por el usuario. En este caso bastaría con algo así:
[php]
$id = intval ( $_GET["id"] );
[/php]
de forma que nos aseguremos de que la variable contenga un valor numérico.
Si en lugar de números tratamos con cadenas, también existen funciones como addslashes que nos permiten “escapar” los caracteres especiales y evitar el SQL injection. Pero esto lo trataremos en otra entrada.


