Ver índice
Seguridad en MySQL

        Ocultar índice  

   Índice de contenidos
   Instalación en Windows
   Instalación en Ubuntu
   Servidores seguros
   Páginas dinámicas
   Sintaxis básica
   Operaciones
   Arrays
   Formatos de presentación
   Operadores
   Bucles
   Extraer y ord. información
   Funciones
   Ficheros externos
   Imágenes dinámicas
   Gestión de directorios
   Cookies y sesiones
   Clases y objetos
   Ficheros en formato PDF
   Bases de datos MySQL
   PHP y XML
   PDO - Bases SQLite / MySQL
   MySQL a traves de misqli
   Algo de JavaScript y AJAX


Inyección de código

Lo conocido como inyección de código o inyección SQL no es otra cosa que una técnica mediante la cual un intruso utiliza las vulnerabilidades de las sentencias MySQL para propósitos distintos a los previstos en la aplicación.

Veamos los siguientes ejemplos. Se trata de una consulta en nuestra tabla demo4 que debe extraer una lista de usuarios cuyo nombre coincida con el que se inserte en en un campo del formulario que se incluye en el documento.

Ver ejemplo Ver ejemplo Ver código fuente

La ejecución de primero de los ejemplos manteniendo el valor por defecto nos dará la respuesta esperada que es la lista de todas las personas de la tabla cuyo nombre coincide con Telesfora. Si hacemos lo mismo con el segundo ejemplo la respuesta será totalmente inesperada. Hemos pedido una lista de personas cuyo nombre coincida con ' or '34=34 y, contra todo pronóstico (no hay nadie que se llame así en nuestra tabla) nos aparece una lista de todos los registros de la tabla.

La explicación es la siguiente. La claúsula WHERE de la consulta contiene lo siguiente: WHERE (Nombre='$nombre'). Al recibir el valor ' or '34=34 sustituirá $nombre por ' or '34=34 y se convertirá en: WHERE (Nombre='' or '34=34'). Es evidente que 34 es siempre igual a 34 como lo sería 13=13 o 1=1. Por lo tanto la condición se cumple sea cual sea el nombre y por esa razón nos aparece la lista de todos los registros. Puede que aquí no tenga demasiada importancia pero imagina que ese formulario se usa para comprobar un nombre de usuario y una contraseña en una tabla de usuarios. Estaríamos accediendo sin dificultad alguna a un lugar al que no nos estaría permitido.

Para evitar la posibilidad de ese efecto indeseado (inyección de código) PHP dispone de la función:

mysql_real_escape_string(cadena)

que hace una llamada a la librería MySQL del mismo nombre y para que «escape» los caracteres especiales contenidos en la cadena de forma que sea mucho más seguro su uso a través de mysql_query(). Los caracteres que son «escapados» son los siguientes: \x00, \n, \r, \, ', " y \x1a. Se convertirían en: \\x00, \\n, \\r, \\\, \', \" y \\x1a con lo cual la cadena ' or '34=34 se convertiría en \' or \'34=34 y la claúsula anteriormente comentada se convertiría en WHERE (Nombre='\' or \'34=34') con lo cual ya no se produciría el efecto indeseado y se evitaría el riesgo de uso inadecuado.

  ¡Cuidado!  

Dado que la función mysql_real_escape_string() se utiliza para hacer una llamada a la librería MySQL del mismo nombre, para su uso es necesario que el servidor MySQL esté activo. De lo contrario se produciría un error.

Si se van a insertar datos binarios es necesario utilizar mysql_real_escape_string() o addslashes() con preferencia de la primera.

Aunque los desarrolladores de PHP anuncian su próxima desaparición, en la actualidad hay en el fichero php.ini una directiva llamada magic_quotes_gpc cuyo valor por defecto es Off. Si cambiáramos esa directiva a On (o si usáramos un servicio externo que la tuviera configurada de esta forma) todos los datos recibidos por medio de los métodos GET o POST serían tratados por la función:

addslashes(cadena)

que devuelve la cadena después de haber «escapado» los carácteres ',", \ y NUL (el byte NULL). La acción de addslashes puede deshacerse utilizando la función

stripslashes(cadena_escapada)

que quita los carácteres de «escape» añadidos por addslashes devolviendo la cadena original.

Para comprobar como está configurada la directiva magic_quotes_gpc disponemos de la función:

get_magic_quotes_gpc()

que devuelve un valor booleano. True si está configurada como On y False en caso de estarlo como Off.

Filtrado de código

Mediante un proceso como el que puedes ver en el código fuente que se incluye a continuación puede realizarse un filtrado de los eventuales intentos de inyección de código. Su funcionamiento es el siguiente:

<?php
 if(is_array($_GET)){
            foreach ($_GET as $_GET_indice=>$_GET_valor){
                    $_GET[$_GET_indice]=filtro_seguridad($_GET_valor);
            }
    }
    if(is_array($_POST) ){
            foreach ($_POST as $_POST_indice=>$_POST_valor){
                    $_POST[$_POST_indice]= filtro_seguridad($_POST_valor);
            }
    }
function filtro_seguridad($valor){
    /* comprueba si están activas las magic_quotes y, en caso de estarlo revierte
       su acción mediante stripslashes */
    if (get_magic_quotes_gpc()) $valor = stripslashes($valor);
    /* por compatibilidad entre versiones Windows - Ubuntu abrimos una conexión
    con MySQL antes de aplicar mysql_real_escape_string */
       $c=mysql_connect("localhost","pepe","pepa");
    /* con la certeza de que estamos ante la cadena original aplicamos la función
       mysql_real_escape_string a todas las cadenas no numéricas */
    if (!is_numeric($valor)) $valor = "'" .mysql_real_escape_string($valor) . "'";
     /* cerramos la conexion y devolvemos el resultado*/
    mysql_close($c);
    return $valor;
}
?>

Hemos guardado este código en un fichero llamado no_inyeccion.inc.php" y hemos hecho dos modificaciones en el ejemplo anterior. Por una parte hemos incluido este fichero mediante la opción include y por otra hemos quitado las comillas a WHERE (Nombre='$nombre') dejándolo como WHERE (Nombre=$nombre). Este es el ejemplo resultante:

Ejecutar consulta Ver código fuente

Borrado de usuarios MySQL

Hasta el momento no hemos modificado ni los usuarios de MySQL ni la configuración de PHPMyAdmin. Aún tenemos los usuarios por defecto (root sin contraseña) y PHPMyAdmin sigue dándonos el mensaje de advertencia relativo al fallo de seguridad de la configuración.

El primer paso para corregir esa vulnerabilidad será borrar los usuarios dejando sólo el usuario pepe que es el que hemos creado al instalar MySQL. Para ello, con el servidor MySQL activo, abriremos http://localhost/phpMyAdmin/ editaremos la tabla user correspondiente a la base de datos mysql y borraremos los usuarios, dejando únicamente al usuario pepe.

Una vez realizados estos cambios, deberíamos cerrar el servidor MySQL y reiniciarlo con la nueva configuración. A partir de aquí, cuando tratamos de acceder de nuevo a PHPMyAdmin nos aparecerá un mensaje de error tal como el que vemos en la imagen.


PHPMyAdmin nos dará este mensaje de error porque su configuración por defecto utiliza el usuario root que ya no existe.
¡Acabamos de borrarlo!

Modificación de la configuración de PHPMyAdmin

Hemos de modificar el fichero config.inc.php que está en el sudirectorio libraires de MyAdmin. Durante el proceso de instalación habíamos hecho algunos cambios en este fichero (algunos de ellos para evitarnos tener que teclear continuamente nombres de usuario y contraseña) que ahora vamos a deshacer.

Fichero inicial    config.inc.php
Guardar como    config.inc.php
Modificaciones en el fichero config.inc.php
LíneaCambios
168 Donde dice:
$cfg['Servers'][$i]['auth_type'] = 'config';
cambiar por:
$cfg['Servers'][$i]['auth_type'] = 'http';
218 Donde dice:
$cfg['Servers'][$i]['nopassword'] = true;
cambiar por:
$cfg['Servers'][$i]['nopassword'] = false;
345 Donde dice:
$cfg['Servers'][$i]['AllowNoPasswordRoot'] = true;
cambiar por:
$cfg['Servers'][$i]['AllowNoPasswordRoot'] =false;

Al cambiar config por http estaremos establecimiento este último valor como método de autentificación de los usuarios. Los otros dos cambios (que ahora hacemos igual a false) significan que no vamos a permitir el acceso sin contraseña a ningún usuario, incluido un eventual usuario root sin contraseña que pudiera quedar registrado desde el proceso inicial de instalación.

Acceso a PHPmyAdmin

A partir del momento que hayamos hecho los cambios anteriores, cada vez que tratemos de acceder a http://localhost/phpmyadmin/ va a aparecer una ventana como la que vemos en la imagen. Será necesario introducir un nombre de usuario válido (pepe en nuestro caso). Nos desaparecerá el mensaje de advertencia y ya podremos gestionar nuestras bases de datos de forma un poco más segura.