2776 votos

¿Cómo puedo prevenir la inyección de SQL en PHP?

Si la entrada del usuario se inserta sin modificación en una consulta SQL, la aplicación se vuelve vulnerable a inyección SQL, como en el siguiente ejemplo:

$unsafe_variable = $_POST['user_input']; 

mysql_query("INSERT INTO `table` (`column`) VALUES ('$unsafe_variable')");

Eso es porque el usuario puede introducir algo como value'); DROP TABLE table;--, y la consulta se convierte en:

INSERT INTO `table` (`column`) VALUES('value'); DROP TABLE table;--')

¿Qué se puede hacer para evitar que esto suceda?

8575voto

Theo Puntos 60103

Uso de sentencias preparadas y consultas con parámetros. Estas son las instrucciones SQL que son enviados y leídos por el servidor de base de datos por separado de los parámetros. De esta manera es imposible que un atacante para inyectar SQL malicioso.

Básicamente tienes dos opciones para conseguir este objetivo:

  1. El uso de PDO:

    $stmt = $pdo->prepare('SELECT * FROM employees WHERE name = :name');
    
    $stmt->execute(array('name' => $name));
    
    foreach ($stmt as $row) {
        // do something with $row
    }
    
  2. El Uso De MySQLi:

    $stmt = $dbConnection->prepare('SELECT * FROM employees WHERE name = ?');
    $stmt->bind_param('s', $name);
    
    $stmt->execute();
    
    $result = $stmt->get_result();
    while ($row = $result->fetch_assoc()) {
        // do something with $row
    }
    

PDO

Tenga en cuenta que cuando se utiliza PDO para acceder a una base de datos MySQL real declaraciones preparadas se no se utiliza por defecto. Para solucionar esto tienes que desactivar la emulación de sentencias preparadas. Un ejemplo de creación de una conexión utilizando el PDO es:

$dbConnection = new PDO('mysql:dbname=dbtest;host=127.0.0.1;charset=utf8', 'user', 'pass');

$dbConnection->setAttribute(PDO::ATTR_EMULATE_PREPARES, false);
$dbConnection->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);

En el ejemplo anterior, el modo de error no es estrictamente necesario, pero se recomienda agregarlo. De esta manera el script no se detiene con un Fatal Error cuando algo va mal. Y le da a los desarrolladores la oportunidad de catch cualquier error(s) thrown PDOExceptions.

¿Cuál es obligatoria , sin embargo, es la primera setAttribute() de la línea, que dice PDO para deshabilitar emulado declaraciones preparadas y uso real de declaraciones preparadas. Esto asegura que la declaración y los valores no son interpretado por PHP antes de enviarlos al servidor de MySQL (dándole un posible atacante no hay oportunidad para inyectar SQL malicioso).

Aunque puede establecer el charset en las opciones del constructor, es importante tener en cuenta que los 'mayores' en versiones de PHP (< 5.3.6) omite el parámetro charset en el DSN.

Explicación

Lo que pasa es que la instrucción SQL que se pasa a prepare se analiza y compila el servidor de base de datos. Mediante la especificación de parámetros ( ? o el nombre de un parámetro como :name en el ejemplo anterior) puede decirle al motor de base de datos donde desea filtrar. Entonces, cuando usted llame execute, el comunicado se combina con los valores de los parámetros que especifique.

Lo importante aquí es que los valores de los parámetros se combinan con el compilado de la declaración, no una cadena SQL. La inyección de SQL obras de engañar a la secuencia de comandos incluyendo maliciosos cadenas cuando se crea SQL para enviar a la base de datos. Así, mediante el envío de la real SQL por separado de los parámetros, se puede limitar el riesgo de acabar con algo que no tenía la intención. Cualquier parámetro que enviar cuando se utiliza una instrucción preparada acaba de ser tratados como cadenas (aunque el motor de base de datos puede hacer en la optimización de parámetros pueden terminar como los números también, por supuesto). En el ejemplo anterior, si el $name variable contiene 'Sarah'; DELETE FROM employees el resultado sería simplemente una búsqueda de la cadena "'Sarah'; DELETE FROM empleados", y usted no va a terminar con una tabla vacía.

Otro de los beneficios con el uso de sentencias preparadas es que si ejecuta la misma instrucción muchas veces en la misma sesión sólo será analiza y compila una vez, le da algunos velocidad de las ganancias.

Oh, y ya que usted le preguntó acerca de cómo hacerlo de una inserción, he aquí un ejemplo (usando PDO):

$preparedStatement = $db->prepare('INSERT INTO table (column) VALUES (:column)');

$preparedStatement->execute(array('column' => $unsafeValue));

1603voto

Matt Sheppard Puntos 32256

Tienes dos opciones : escapar los caracteres especiales en sus unsafe_variable, o mediante una consulta parametrizada. Ambos se protegen de la inyección de SQL. La consulta parametrizada se considera la mejor práctica, pero escapando de caracteres en la variable requieren menos cambios.

Vamos a hacer más simple el escapado de cadenas de primero.

//Connect

$unsafe_variable = $_POST["user-input"];
$safe_variable = mysql_real_escape_string($unsafe_variable);

mysql_query("INSERT INTO table (column) VALUES ('" . $safe_variable . "')");

//Disconnect

Véase también, los detalles de la mysql_real_escape_string función.


Advertencia:

A partir de PHP 5.5.0 mysql_real_escape_string y a las mysql de extensión están en desuso. Por favor, use mysqli de extensión y mysqli::escape_string función en lugar


El uso de la consulta con parámetros, es necesario utilizar MySQLi en lugar de la de MySQL funciones. Para reescribir su ejemplo, tendríamos algo como lo siguiente.

<?php
    $mysqli = new mysqli("server", "username", "password", "database_name");

    // TODO - Check that connection was successful.

    $unsafe_variable = $_POST["user-input"];

    $stmt = $mysqli->prepare("INSERT INTO table (column) VALUES (?)");

    // TODO check that $stmt creation succeeded

    // "s" means the database expects a string
    $stmt->bind_param("s", $unsafe_variable);

    $stmt->execute();

    $stmt->close();

    $mysqli->close();
?>

La función de la tecla que usted querrá leer no habría mysqli::prepare.

También, como otros han sugerido, usted puede encontrar útil/más fácil el paso de una capa de abstracción con algo como PDO.

Por favor, tenga en cuenta que el caso que se pregunta es bastante simple, y que los casos más complejos pueden requerir enfoques más complejos. En particular:

  • Si desea modificar la estructura de la SQL basado en la entrada del usuario, parametrizar las consultas no van a ayudar, y el escape necesarios no están cubiertos por mysql_real_escape_string. En este tipo de caso que usted sería mejor pasar la entrada del usuario a través de una lista blanca para asegurarse de que sólo 'caja fuerte' valores permitidos a través de.
  • Si utiliza números enteros a partir de la entrada del usuario en una condición y tomar la mysql_real_escape_string enfoque, usted va a sufrir el problema que se describe por Polinomio en los comentarios de abajo. Este caso es más complicado porque enteros de no estar rodeado por comillas, por lo que se le podía hacer frente, mediante la validación de que la entrada de usuario contiene sólo dígitos.
  • Hay otros casos que no soy consciente. Usted puede encontrar http://webappsec.org/projects/articles/091007.txt un recurso útil en algunos de los problemas más sutiles que usted puede encontrar.

1033voto

Cada respuesta aquí cubre sólo una parte del problema.
De hecho, hay cuatro de consulta diferentes partes que podemos añadir de forma dinámica:

  • una cadena de
  • un número de
  • un identificador
  • la sintaxis de la palabra clave.

instrucciones preparadas y cubre sólo 2 de ellos

Pero a veces tenemos que hacer nuestra consulta aún más dinámico, la adición de los operadores o identificadores así.
Por lo tanto, vamos a necesitar diferentes técnicas de protección.

En general, un enfoque de la protección se basa en la creación de listas blancas. En este caso, cada parámetro dinámico debe ser codificado en la secuencia de comandos y elegido de ese conjunto.
Por ejemplo, para hacer la dinámica de pedido:

$orders  = array("name","price","qty"); //field names
$key     = array_search($_GET['sort'],$orders)); // see if we have such a name
$orderby = $orders[$key]; //if not, first one will be set automatically. smart enuf :)
$query   = "SELECT * FROM `table` ORDER BY $orderby"; //value is safe

Sin embargo, hay otra forma de asegurar los identificadores de la - fuga. Mientras usted tiene un identificador citado, puede escapar comillas simples inclinadas en el interior de la duplicación de ellos.

Como un paso más podemos pedir prestado un realmente brillante idea de usar algún marcador de posición (un proxy para representar el valor real de la consulta) de la preparación de declaraciones y de inventar un marcador de posición de otro tipo - un identificador de marcador de posición.

Así, para hacer una larga historia corta: es un marcador de posición, no comunicado , puede ser considerado como una bala de plata.

Así, una recomendación general puede ser formulada como
Mientras que usted está agregando partes dinámicas para la consulta uso de marcadores de posición (y estos marcadores de posición se procesa correctamente, por supuesto), usted puede estar seguro de que su consulta es seguro.

Todavía hay un problema con la sintaxis de SQL palabras clave (como AND, DESC y tal), pero la creación de listas blancas parece que el único enfoque en este caso.

820voto

Kibbee Puntos 36474

Me gustaría recomendar el uso de PDO (PHP Data Objects) para ejecutar consultas SQL con parámetros.

Esto no sólo protege contra ataques de inyección SQL, también acelera las consultas.

Y mediante el uso de PDO en lugar de mysql_, mysqli_y pgsql_ funciones, su aplicación un poco más extraída de la base de datos, en el caso poco probable de tener que cambiar de base de datos de proveedores.

608voto

Imran Puntos 20117

El uso de las DOP y las consultas preparadas.

($conn es un objeto PDO)

$stmt = $conn->prepare("INSERT INTO tbl VALUES(:id, :name)");
$stmt->bindValue(':id', $id);
$stmt->bindValue(':name', $name);
$stmt->execute();

Iteramos.com

Iteramos es una comunidad de desarrolladores que busca expandir el conocimiento de la programación mas allá del inglés.
Tenemos una gran cantidad de contenido, y también puedes hacer tus propias preguntas o resolver las de los demás.

Powered by:

X