Ver Mensaje Individual
  #3 (permalink)  
Antiguo 06/09/2012, 05:43
Avatar de Triby
Triby
Mod on free time
 
Fecha de Ingreso: agosto-2008
Ubicación: $MX->Gto['León'];
Mensajes: 10.106
Antigüedad: 15 años, 11 meses
Puntos: 2237
Respuesta: [Aporte] Seguridad básica en PHP

Séptima Parte -> Inyección SQL

Cita:
NOTA IMPORTANTE:


El uso de la librería mysql está desaconsejado, por lo que nos basaremos exclusivamente en mysqli y PDO.

Más info: http://www.forosdelweb.com/f18/anunc...ecada-1008145/

Si usas un motor de base de datos diferente de MySQL, revisa el manual de PHP para la librería correspondiente, donde encontrarás las funciones adecuadas para evitar ataques.
Por seguridad y para evitar errores, los datos a incluir en una consulta SQL deben ser "escapados" o "preparados".

Veamos la sintáxis de algunas consultas comunes:

Código MySQL:
Ver original
  1. SELECT * FROM tabla WHERE cadena = 'Este valor es una cadena';
  2. INSERT INTO tabla SET cadena = 'Este valor es una cadena';
  3. INSERT INTO tabla (cadena) VALUES('Este valor es una cadena');
  4. UPDATE tabla SET cadena = 'Este valor es una cadena' WHERE id = 5;

Supongamos que 'Este valor es una cadena' lo enviamos por medio de la variable $cadena y que podemos obtener el valor a través de un formulario o de otra consulta a la base de datos, lo mismo para el número 5 usado en id.

Si los valores son como en el ejemplo, no tendremos problemas para ejecutar la consulta, pero, qué pasa si tenemos algo como:

Código PHP:
Ver original
  1. $cadena "Esta cadena contiene ' comilla simple";
  2. $id = 'número';

Las consultas quedarían así:

Código MySQL:
Ver original
  1. SELECT * FROM tabla WHERE cadena = 'Esta cadena contiene ' comilla simple';
  2. INSERT INTO tabla SET cadena = 'Esta cadena contiene ' comilla simple';
  3. INSERT INTO tabla (cadena) VALUES('Esta cadena contiene ' comilla simple');
  4. UPDATE tabla SET cadena = 'Este valor es una cadena' WHERE id = 'numero';

Este es el menos malo de los escenarios, porque al tratar de ejecutar la consulta sólo obtendremos un error de sintáxis porque:
- La comilla simple hace que termine el valor de cadena y lo que viene después es "basura" en la consulta
- 'número' no se puede asignar a id porque se trata de un campo numérico

Lo de la variable $id se puede solucionar fácilmente como vimos antes, forzando a que sea entero y validando, pero, si tenemos esto:

Código PHP:
Ver original
  1. $cadena = "cualquier cosa'; INSERT INTO usuarios (usuario, password, rol) VALUES ('Triby', 'contraseña', 'admin'); #";

La primera consulta UPDATE quedaría así (los saltos de línea son para verlo mejor):

Código MySQL:
Ver original
  1. UPDATE tabla SET cadena = 'cualquier cosa';
  2. INSERT INTO usuarios (usuario, password) VALUES ('Triby', 'contraseña');
  3. # WHERE id = 5

1- Se ejecuta UPDATE estableciendo el valor de cadena en 'cualquier cosa' para todos los registros de la tabla, la comilla simple corta el contenido del campo.
2- El punto y coma le indica al motor de base de datos que finalizó la primera consulta y continúa otra.
3- Se ejecuta INSERT INTO, registrando al usuario, obteniendo permisos de administrador.
4- El punto y coma separa la siguiente consulta que, por el signo de número (#) es un comentario y no se ejecuta.

Algunas soluciones posibles con mysqli:

NOTA: Mysqli por procedimientos también está considerado obsoleta, hay que usar orientado a objetos o PDO.

Código PHP:
Ver original
  1. // Recordar que previamente se deben limpiar todas las entradas
  2. // Para evitar ataques XSS y caracteres no permitidos
  3. $cadena = $_POST['cadena'];
  4.  
  5. // Usando mysqli estructurado
  6. // addslashes() no es suficiente, mejor usamos la función nativa de la librería
  7. $cadena = mysqli_real_escape_string($cadena);
  8. $sql = "SELECT * FROM tabla WHERE cadena = '$cadena'";
  9. $resultados = mysqli_query($sql);
  10.  
  11. // Usando mysqli orientado a objetos
  12. $mysqli = new mysqli('localhost', 'usuario', 'contraseña', 'base_de_datos');
  13. $cadena = $mysqli->real_escape_string($cadena);
  14. $sql = "SELECT * FROM tabla WHERE cadena = '$cadena'";
  15. $resultados = $mysqli->query($sql);
  16.  
  17. // Usando mysqli con consultas preparadas y parámetros enlazados
  18. $mysqli = new mysqli('localhost', 'usuario', 'contraseña', 'base_de_datos');
  19. $sql = "SELECT * FROM tabla WHERE cadena = ?";
  20. $stmt = $mysqli->prepare($sql);
  21. // Agregamos el valor de $cadena como string a la consulta preparada
  22. $stmt->bind_param('s', $cadena);
  23. $stmt->execute();

Hay otras opciones con mysqli y si requieres más info, ingresa en: http://www.php.net/manual/es/book.mysqli.php

Algunas soluciones posibles con PDO:

Código PHP:
Ver original
  1. // Recordar que previamente se deben limpiar todas las entradas
  2. // Para evitar ataques XSS y caracteres no permitidos
  3. $cadena = $_POST['cadena'];
  4.  
  5. // Info para conectar a MySQL
  6. $dbhost = 'mysql:dbname=pruebas;host=localhost';
  7. $dbuser = 'usuario';
  8. $dbpass = 'contraseña';
  9.  
  10. // Establecer conexión y seleccionar base de datos
  11. $db = new PDO($dbhost, $dbuser, $dbpass);
  12.  
  13. // Parámetros con nombre
  14. $sql = "SELECT * FROM tabla WHERE cadena = :cadena";
  15. $sth = $db->prepare($sql);
  16. $sth->execute(array(':cadena' => $cadena));
  17.  
  18. // Parámetros con signo de interrogación
  19. $sql = "SELECT * FROM tabla WHERE cadena = ?";
  20. $sth = $db->prepare($sql);
  21. $sth->execute(array($cadena));
  22.  
  23. // Enlazando parámetros
  24. $sql = "SELECT * FROM tabla WHERE cadena = :cadena";
  25. $sth = $db->prepare($sql);
  26. $sth->bindParam(':cadena', $cadena, PDO::PARAM_STR, 50);
  27. $sth->execute();

Como habrás podido notar, PDO con parámetros enlazados es la mejor opción, ya que te permite especificar el tipo y longitud del dato que se agregará a la consulta.

Hay otras opciones con PDO y si requieres más info, ingresa en: http://www.php.net/manual/es/book.pdo.php

Nota: Usando parámetros con nombre o con signo de interrogación, PDO trata todos los campos como cadenas.

-- Continuará --
__________________
- León, Guanajuato
- GV-Foto

Última edición por Triby; 06/02/2013 a las 13:02