Ver índice
Seguridad con MySQLi

        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

El hecho de utilizar los recursos de MySQLi no excluye los riesgos de seguridad derivados de lo que llamábamos inyección de código cuando tratábamos de MySQL. Todo lo argumentado allí respecto a los riesgos y vulnerabilidades sigue teniendo plena vigencia tal como puedes ver en estos ejemplos.

Ejecutar consulta
Mediante procesos
Ver código fuente
Mediante procesos
Ejecutar consulta
Mediante objetos
Ver código fuente
Mediante objetos

Como habrás podido observar una consulta con el código adecuado WHERE Nombre='' or '34=34' muestra la vulnerabilidad y devuelve todos los registros contenidos en la tabla por las mismas razones ya comentadas al estudiar MySQL.

Para evitar la posibilidad de ese efecto indeseado (inyección de código) cuando utilizamos MySQLi, PHP dispone de las función:

mysqli_real_escape_string($conexion,cadena)
o
$objeto->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.

Ejecutar consulta
Mediante procesos
Ver código fuente
Mediante procesos
Ejecutar consulta
Mediante objetos
Ver código fuente
Mediante objetos


  ¡Cuidado!  

El uso de la función mysqli_real_escape_string() requiere como parámetro un identificador de conexión. Por lo tanto es imprescindible que antes de utilizarla se haya establecido una conexión con el servidor MySQL.
De igual manera, cuando se trata de programación orientada a objetos, real_escape_string() es un método y en consecuencia es requisito inprescindible la preexistencia del objeto antes de su utilización.

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);
    /* Abrimos una conexión con MySQL requerida para aplicar mysqli_real_escape_string */
       $conexion=mysqli_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 = "'" .mysqli_real_escape_string($conexion,$valor) . "'";
     /* cerramos la conexion y devolvemos el resultado*/
    mysqli_close($conexion);
    return $valor;
}
?>

Este es un ejemplo de utilización de procedimiento anterior

Ejecutar consulta
Mediante procesos
Ver código fuente
Mediante procesos

En el caso de optar por la programación orientada a objetos el script anterior debería tener esta sintaxis:

<?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);
    /* Abrimos una conexión con MySQL requerida para aplicar mysqli_real_escape_string */
       $objeto_filtro=new mysqli("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 = "'" .$objeto_filtro->real_escape_string($valor) . "'";
     /* cerramos la conexion y devolvemos el resultado*/
    $objeto_filtro->close();
    return $valor;
}
?>

Este es un ejemplo de utilización de procedimiento anterior

Ejecutar consulta
Mediante objetos
Ver código fuente
Mediante objetos