¿Cómo evito la inyección de sql con php y mysql?

Tengo un formulario en el que el visitante puede ingresar datos, y quiero almacenar estos datos en una base de datos mysql a través de la variable $ _POST. ¿Qué necesito para evitar la inyección de sql?

Use declaraciones preparadas .

Hice una presentación en la conferencia PHP TEK-X en mayo de 2010 sobre este tema e intenté abarcar varios métodos para defenderme contra la inyección de SQL. No existe un método único que sea el mejor en todos los casos, por lo que debe aprender múltiples métodos y usarlos todos:

  • Valide la entrada del usuario o cualquier otro contenido de fonts externas (incluso datos de su propia base de datos) antes de interpolarlo en una consulta SQL. Puede usar la extensión de filtro de PHP o expresiones regulares, por ejemplo.

  • Forzar que el contenido externo esté en el formato correcto. Por ejemplo, (int) $_POST["userid"] tipos de letra cuyo contenido es un entero simple, por lo que es seguro de usar.

  • Al incluir contenido dynamic en lugar de valores literales en expresiones de SQL, use consultas preparadas con parámetros. Tenga en cuenta que la extensión simple de mysql en PHP no admite parámetros de consulta: use PDO . No uso mysqli porque su API es inconsistente y difícil de usar.

  • Al usar el predicado IN() , no puede usar un parámetro para una lista de valores. Concaten marcadores de posición de parámetros múltiples, tantos como tenga valores en su lista. Esto no es difícil, solo toma una línea o dos de código:

     $sql = "SELECT ... FROM ... WHERE user_id IN (" . join(",", array_fill(0,count($userid_list),"?")) . ")"; $pdoStmt = $pdo->prepare($sql); $pdoStmt->execute($userid_list); 
  • Al usar nombres dynamics de tablas, nombres de columnas o palabras clave SQL, no puede usar parámetros de consulta. Tienes que interpolar contenido dynamic. Pero puede usar técnicas de listas blancas para asignar el contenido que no es de confianza a identificadores legales y seguros y palabras clave.

Vea mi presentación Mitos y falacias de inyección de SQL para obtener más información y ejemplos.

También es posible que le guste mi nuevo libro SQL Antipatterns: evitar las trampas de la progtwigción de bases de datos . Mi libro tiene un capítulo sobre Inyección SQL.

Debe seguir algunas reglas al agregar cualquier dato a la consulta, no importa de dónde provenga, ya sea de usuario o formulario o de cualquier otra cosa. Las reglas siempre son las mismas.

Para enviar una consulta a la base de datos, tiene 2 opciones:

  1. Cree una consulta de la manera habitual, para que se vea exactamente como la consulta SQL que puede ejecutar en la consola sql.
    Para hacerlo, uno debe entender un conjunto completo de reglas , no solo “usar mysql_real_escape_string”.
    Reglas tales como:

    • las cadenas deben estar entre comillas y escapadas. ese es el único significado de escapar: ¡son solo delimitadores fáciles! (y algunos otros caracteres: cadena de caracteres de terminación y carácter de escape en sí). Sin citas circundantes, mysql_real_escape_string es inútil.
    • los números deben enviarse a su tipo explícitamente. Aunque los números de datos se pueden amenazar como las cadenas, hay algunos números, como los parámetros de la cláusula LIMIT, que no se pueden escapar y solo se pueden transmitir.
  2. Para enviar consulta y datos por separado .
    Esta es la forma más preferida, ya que se puede acortar para simplemente “usar enlace”. Todas las cadenas, números y parámetros LIMIT se pueden vincular, no se preocupe en absoluto.
    Con este método, su consulta con marcadores de posición se envía a la base de datos tal cual, y los datos vinculados se envían en paquetes separados, por lo que no puede interferir. Es como la separación de código y datos . Envía su progtwig (consulta misma) separado de los datos.

Todo lo dicho anteriormente cubre solo la inserción de datos. Pero a veces tenemos que hacer que nuestra consulta sea aún más dinámica, agregando operadores o identificadores.
En este caso, todos los parámetros dynamics deben estar codificados en nuestro script y elegidos de ese conjunto.
Por ejemplo, para hacer un pedido dynamic:

 $orders = array("name","price","qty"); $key = array_search($_GET['sort'],$orders)); $orderby = $orders[$key]; $query = "SELECT * FROM `table` ORDER BY $orderby"; 

o búsqueda dinámica:

 $w = array(); $where = ''; if (!empty($_GET['rooms'])) $w[]="rooms='".mesc($_GET['rooms'])."'"; if (!empty($_GET['space'])) $w[]="space='".mesc($_GET['space'])."'"; if (!empty($_GET['max_price'])) $w[]="price < '".mesc($_GET['max_price'])."'"; if (count($w)) $where="WHERE ".implode(' AND ',$w); $query="select * from table $where"; 

en este ejemplo, estamos agregando a la consulta únicamente los datos ingresados ​​por el usuario, no los nombres de campo, que están todos codificados en el script. Para el enlace el algoritmo es muy similar

Y así.