¿Cómo evitar la inyección SQL en PHP?

By julio 25, 2018PHP

Las sentencias dinámicas son sentencias SQL que se crean como cadenas de texto (strings) y en las que se insertan/concatenan valores obtenidos de alguna fuente (normalmente proveniente del usuario), lo que puede hacer que sean vulnerables a inyección SQL si no se sanean las entradas, como por ejemplo:

Eso es un ejemplo de una vulnerabilidad grave en la seguridad de una aplicación (web o no) porque si el usuario introdujese un valor como  1; DROP TABLE usuarios;-- nos encontraríamos con que la sentencia ejecutada sería:

[/crayon] Y se eliminaría la tabla Usuarios con todos los datos contenidos en ella.

¿Cómo puedo evitar que la inyección SQL ocurra en PHP?

NO USES SENTENCIAS DINÁMICAS NI FUNCIONES  mysql_*

Las funciones  mysql_* ( mysql_connectmysql_query, etc.) son inseguras por naturaleza y su uso no sólo no está recomendado, sino que se consideran obsoletas y se han eliminado completamente a partir de PHP7.

Incluso los métodos nativos que existen en PHP para sanear las entradas de usuario (como  mysql_real_escape_string) pueden presentar (raros) problemas y fallar en algunos casos como cuando se usan codificación de caracteres diferentes a UTF-8 junto a versiones no actualizadas de MySQL (en las páginas de PHP para estas funciones se avisa de este riesgo).

Usa sentencias preparadas y consultas parametrizadas

Aunque se podrían sanear las entradas usando métodos como  mysqli_real_escape_string, es más recomendable la utilización de sentencias preparadas o parametrizadas. Las sentencias preparadas te permitirán ejecutar la misma sentencia con gran eficiencia.

En PHP, tienes dos alternativas principales: PDO y MySQLi. Hay varias diferencias entre ambas, pero la principal es que PDO se puede usar con diferentes tipos de base de datos (dependiendo del driver utilizado) mientras que MySQLi es exclusivamente para bases de datos MySQL. Es por ello que recomendaría PDO sobre MySQLi.

PDO

Los marcadores de posición (que indican dónde se sustituirá una cadena por su valor), se pueden definir bien usando un signo de interrogación ( ?) o bien usando un nombre (generalmente empezando con  :). Personalmente prefiero usar un nombre, porque eso me ayuda a encontrar posibles errores en caso de tener múltiples variables.

Aquí dejo un ejemplo:

En este caso,  :idusuario se sustituirá por el valor de  $_POST["id_usuario"] de forma segura, y cuando hace el bind se indica que la variable es de tipo entero ( PDO::PARAM_INT).

Nota: si la variable es una cadena de texto se usará  PDO::PARAM_STR y no hace falta poner las comillas en la sentencia SQL; al especificarle a PHP que es una cadena, las añadirá automáticamente al hacer el bind.

En caso de que existan varias variables a incluir en la sentencia SQL, se debe incluir un único parámetro para cada uno de los valores que se usan en la sentencia. Del ejemplo anterior, el  :id_usuario puede usarse una única vez en la consulta que se esta preparando. Si fuera necesario usar el “id_usuario” de nuevo en la consulta, se debe crear otro parámetro con el valor de  $usuario_id.

MySQLi

Este método tiene dos interfaces: una procedural y otra orientada a objetos. La interfaz procedural es muy parecida a  mysql_*, y por ello la gente que migra desde  mysql_* puede sentirse atraída por la facilidad que  mysqli_* ofrece. Aunque, de nuevo personalmente, optaría por la versión POO.

Nota: aunque las funciones  mysqli_* suelen ser parecidas a las  mysql_*, en algunos casos pueden tener diferentes parámetros de entrada o diferentes salidas, lo que puede llevar a algo de confusión al principio.

El ejemplo de la pregunta quedaría así con MySQLi en su interfaz orientada a objetos:

Como se puede ver, es bastante parecido a PDO (cambia un poco cómo se especifica el tipo de valor,  i para enteros y  s para cadenas, pero la idea es similar).

En la versión procedural de MySQLi, el código equivalente sería:

Fuente y bibliografía para más información en español:

jaimefranko

Author jaimefranko

¡Hola! mi nombre es Jaime Franco soy Full Stack Developer, desde el 2010 estoy creando proyectos en diferentes áreas de este maravilloso mundo digital, desde entonces puedo decir que cada día se aprende algo nuevo.

More posts by jaimefranko
WhatsApp #wasapeame