87

He encontrado bastantes preguntas en StackOverflow sobre programas o formularios web que guardan información en una base de datos (especialmente en PHP y MySQL) y que contienen graves problemas de seguridad relacionados principalmente con la inyección SQL.

Normalmente dejo un comentario y/o un enlace a una referencia externa, pero un comentario no da espacio para mucho y sería positivo que hubiera una referencia interna en SOes sobre el tema así que decidí escribir esta pregunta/respuesta: ¿Qué es la inyección SQL y cómo evitarla?

Ariel Montes
  • 2,352
  • 1
  • 5
  • 27
Alvaro Montoro
  • 48,157
  • 26
  • 100
  • 179
  • 1
    La inyección SQL es cuando intentan ejecutar un script de SQL a través de una URL – mcontartesi May 20 '16 at 05:09
  • 2
    Relacionado: [¿Cómo evitar la inyección SQL en PHP?](https://es.stackoverflow.com/questions/18232/c%C3%B3mo-evitar-la-inyecci%C3%B3n-sql-en-php?noredirect=1&lq=1) – Francisco Romero Nov 17 '17 at 09:48
  • 1
    Para evitar la inyeccion de sql, valida tus inputs para que no puedan meter caracteres especiales como -- '' ; etc. – Alejandro Feb 28 '18 at 23:45
  • 1
    Simple, con el uso de procedimientos almacenados. – Oscar May 20 '18 at 02:56
  • 1
    @RicardoRuizBenites los procedimientos almacenados no previenen la inyección SQL por sí solos. Si la sentencia se sigue generando de forma dinámica dentro del procedimiento, el código seguirá siendo vulnerable a inyección SQL. – Alvaro Montoro May 20 '18 at 06:17
  • 2
    ¿Responde esto a tu pregunta? [¿Cómo evitar la inyección SQL en PHP?](https://es.stackoverflow.com/questions/18232/c%c3%b3mo-evitar-la-inyecci%c3%b3n-sql-en-php) – Shassain Jul 09 '20 at 04:09

3 Answers3

102

¿Qué es la inyección SQL?

La inyección de SQL es un tipo de ataque informático que consiste en la infiltración de código intruso dentro de las sentencias/consultas a ejecutar en la base de datos. Esta infiltración suele ocurrir a través de los parámetros pasados por los usuarios a un programa o a través de un formulario web.

El objetivo de la intrusión suele ser malicioso y puede causar daños importantes: revelación de datos, modificación de contenido, suplantación de identidad, borrado y destrucción de datos o estructuras dentro de la base de datos...

Muchos programadores asumen que los usuarios siempre van a introducir datos válidos y que las consultas por tanto van a ser fiables, y no toman medidas especiales a la hora de armar sentencias que ejecutan en la base de datos. Eso hace sus aplicaciones vulnerables a este tipo de ataques.

Para encontrar más información en español sobre inyección SQL lee el artículo de la Wikipedia (con ejemplos de resolución en diferentes lenguajes), la documentación oficial de PHP o el sitio web de OWASP.

EJEMPLO: imagina que tienes una página web con un formulario para actualizar los datos del usuario en tu base de datos, pero no saneas esas entradas antes de utilizarlas. En PHP podrías tener algo como esto:

$sql = "UPDATE usuarios SET nombre = '".$_POST["nombre"]."' WHERE id = ".$_POST["id"];

Ahora imagina que el usuario malicioso escribe que su nombre es Pepito' WHERE 1=1;--. Tu sentencia SQL quedará así:

UPDATE usuarios SET nombre = 'Pepito' WHERE 1=1;--' WHERE id = 123

Los caracters -- indican que lo que sigue es un comentario y se ignora en la base de datos, por lo que se ejecuta es simplemente esto:

UPDATE usuarios SET nombre = 'Pepito' WHERE 1=1;

El usuario malicioso ha inyectado código SQL y ha hecho que todos los usuarios en tu base de datos se llamen Pepito a partir de ahora. Si además permites múltiples sentencias a la misma vez, el usuario malicioso podría pasar algo como '; DROP TABLE usuarios;-- y borrar toda la tabla, como se bromea (aunque no sería para broma) en esta tira clásica de xkcd:

introducir la descripción de la imagen aquí


¿Cómo puedo evitar la inyección SQL?

Para evitar la inyección SQL basta con seguir una serie sencilla de directrices:

  • DESCONFÍA siempre de las entradas del usuario. Preprocésalas, sanéalas o compruébalas, pero nunca las uses directamente. Debes asumir siempre que el usuario va a intentar atacar tu base de datos; queremos pensar que todos los usuarios son buenas personas, pero basta un único usuario malicioso para destruirlo todo.

  • EVITA los SQL dinámicos. Son el error más común a la hora de ejecutar sentencias a la base de datos y es lo que aprovechan los usuarios maliciosos para atacar tu código. La solución es fácil: no concatenes la consulta SQL con las entradas del usuario, y en su lugar

  • UTILIZA sentencias preparadas (también llamadas parametrizadas). Ofrecen una estrategia más eficiente y menos propensa a errores. Además, los principales sistemas de bases de datos modernos soportan sentencias preparadas con variables vinculadas.

  • LIMITA el acceso a la base de datos. No utilices superusuarios (root) sino usuarios con acceso personlizado/limitado a la base de datos (aunque esto no siempre está al alcance de todos los desarrolladores).

  • MODERNIZA tu código. Mantén tu código al día en materia de seguridad, no utilices métodos obsoletos o no recomendados. Hay motivos por los que están obsoletos.

    Por ejemplo, un error relacionado con la modernización que se ve frecuentemente en StackOverflow es el caso particular de PHP y las funciones mysql_*, que deberían evitarse y usar MySQLi (mysqli_*) o PDO en su lugar. Para más información sobre ese tema, lee la pregunta ¿Cómo evitar la inyección SQL en PHP?

Alvaro Montoro
  • 48,157
  • 26
  • 100
  • 179
  • 1
    @Alvaro ¿Cómo puede el usuario malicioso insertar una sentencia SQL en el código PHP? No me quedo muy claro eso. – Hoose Aug 16 '16 at 07:32
  • 1
    @AlvaroMontoro Existen muchos temas de programación pero cual es más segura sentencias preparadas, php orientados a objectos o php con clases??? Pero orientándonos solo en Mysqlí. Deberíamos abrir una pregunta como esta??? – Otto Aug 19 '16 at 13:24
  • 2
    @J.Doe No me queda muy claro qué es lo que quieres. En MySQLi, el método procedural y el orientado a objetos son equivalentes (y ambos soportan sentencias preparadas). Podrías crear una pregunta, pero tendrás que añadir más detalles. – Alvaro Montoro Aug 19 '16 at 13:38
  • 1
    @Hoose tienes una aplicación web donde una página realiza una búsqueda y el usuario tiene una caja de texto para ingresar el texto de búsqueda. Si tu armas el query así: "SELECT * FROM tabla WHERE col like CONCAT('%', CONCAT('" + inputText.value() + "', '%'));" entonces ya tienes un query abierto a inyección SQL. Por ejemplo, buscar "perro" o "amigo" no genera problemas, pero si ingresas "')); --" te traes toda la información de la tabla (llegar a ese código es muy fácil si no tienes un buen manejo de errores). Y luego puedes explotar aún más la aplicación –  Sep 08 '16 at 17:35
10

Trata de utilizar siempre parámetros en la consulta, nunca concatenes las variables directamente sobre la consulta, ejemplo:

string consulta = "SELECT * FROM Usuarios WHERE UserName = '"+txtUserName.Text+"';";

La opción que evita eso es con parametros y dependiendo del lenguaje asignas la variable:

string consulta = "SELECT * FROM Usuarios WHERE UserName = @UserName;";

Espero que te ayude esta información.

Frank Leao
  • 109
  • 1
  • 2
5

Si utilizas php una buena forma de prevenir el SQLinjection es utilizando la funcion preg_match nativa de php, te haces de un array con palabras de sql reservadas y algunas otras, recuerda que la inyección de sql no es el unico ataque que puede recibir un sistema web, o una pagina, como quieras llamarlo, y con este se parea todo dato que envía el usuario, nadie ingresa en un nombre o direccion o lo que sea, palabras como select o union, que concatena mas de un select

select 1 from dual;
union all
select 2 from dual;

y tambien atrapar caracteres raros como / ' % &, etc para que no pudieran ni por casualidad dañar tu sistema o tu información. Aquí esta la api de preg match http://php.net/manual/es/function.preg-match.php suerte. saludos

  • 1
    Tambien puedes utilizar la funcion strip_tags, que quitara todas las etiquetas html y otras, aunque el alcance que posee esta funcion no es muy alto, ademas de esta manera de hacerlo, para añadir más seguridad también puedes implementar el mismo filtro en js solo por prevención, la seguridad en la informática se trata sólo de supuestos, recuerda que nunca es mucha seguridad – Francisco Rolando Jul 27 '16 at 19:28
  • 4
    ¿Podrías poner un ejemplo de cómo se haría? Por lo que tenía entendido, `preg_match` no era un método recomendado para prevenir inyección SQL porque depende mucho de cada tipo de entrada y de la habilidad del programador a la hora de escribir expresiones regulares. – Alvaro Montoro Jul 28 '16 at 12:59
  • 1
    Esto es justamente lo que **no se debe hacer**. De ninguna manera. Jamás! – Mariano Aug 03 '16 at 04:21