Qué es SQL Injection y cómo prevenirlo

Autor: Publicado

En breve: SQL Injection es una vulnerabilidad que permite alterar una consulta a una base de datos mediante datos de entrada preparados para ello. Surge principalmente cuando la aplicación concatena instrucciones SQL con texto proporcionado por el usuario. La protección básica son las consultas parametrizadas, que separan la instrucción de los datos.

Cómo los datos del usuario se convierten en una instrucción

SQL es un lenguaje que permite leer y modificar datos en bases de datos relacionales. Una aplicación puede utilizarlo para buscar una cuenta, recuperar un pedido o guardar una nueva dirección de un cliente. SQL Injection, o inyección de SQL, se produce cuando los datos de entrada se interpretan como parte de la sintaxis de una instrucción.

Supongamos que una aplicación busca una cuenta por su nombre de usuario. El programador escribe el inicio de la consulta, concatena el valor del formulario y lo cierra con una comilla simple. Un nombre de usuario normal produce el resultado esperado, pero un texto que contenga una comilla simple y una condición adicional puede cambiar el sentido de toda la búsqueda.

El valor peligroso no tiene por qué proceder de un formulario de inicio de sesión. También puede llegar a través de un parámetro de una URL, un filtro de productos, una cabecera de la petición o datos guardados previamente en la base de datos. Lo importante es cómo utiliza la aplicación ese valor, no de dónde lo ha obtenido.

Un experimento sencillo en Linux

El siguiente ejemplo solo utiliza una base de datos temporal en la memoria del proceso. No se conecta a ninguna web ni a ningún servidor externo. Muestra cómo se pueden leer más registros de los previstos, no un mecanismo completo de inicio de sesión.

Necesitas Python 3 con el módulo sqlite3 disponible. Puedes comprobarlo ejecutando este comando en la terminal:

python3 -c "import sqlite3; print(sqlite3.sqlite_version)"

Si el comando falla, consulta la documentación de tu distribución para saber cómo instalar Python 3 o el módulo que falta. No es necesario iniciar un servidor de base de datos independiente.

Ejecuta el bloque completo en la terminal:

python3 - <<'PY'
import sqlite3

conexion = sqlite3.connect(":memory:")
polaczenie.execute(
    "CREATE TABLE cuentas (id INTEGER PRIMARY KEY, login TEXT)"
)
polaczenie.executemany(
    "INSERT INTO cuentas (login) VALUES (?)",
    [("ana",), ("pedro",)]
)

valor = "' OR 1=1 -- "
consulta = "SELECT id, login FROM cuentas WHERE login = '" + valor + "'"

print("Consulta:", consulta)
print("Resultado:", polaczenie.execute(consulta).fetchall())
polaczenie.close()
PY

El resultado contiene ambas cuentas, aunque ninguna tiene un nombre de usuario que coincida con el valor introducido. La comilla simple cierra el texto del nombre de usuario, OR 1=1 añade una condición que siempre es verdadera y -- inicia un comentario, por lo que la comilla simple final deja de afectar a la consulta.

Este código es deliberadamente vulnerable y está destinado a una demostración local. No pruebes entradas similares en servicios ajenos sin una autorización expresa que cubra el alcance de las pruebas.

Cómo corregir el mismo código

Una consulta parametrizada contiene marcadores que indican dónde van los datos, mientras que los valores se pasan por separado a la biblioteca que gestiona la base de datos. En el módulo sqlite3, uno de esos marcadores puede ser ?. La biblioteca trata el valor recibido como datos, no como sintaxis adicional de SQL.

En el ejemplo anterior, sustituye la construcción y ejecución de la consulta por este fragmento, manteniéndolo antes de polaczenie.close():

consulta = "SELECT id, login FROM cuentas WHERE login = ?"
resultado = polaczenie.execute(consulta, (valor,)).fetchall()
print("Resultado:", resultado)

Ahora obtendrás una lista vacía: la base de datos busca un nombre de usuario literal que contenga todo el texto recibido. Si cambias el valor de valor por "ana", la consulta devolverá únicamente la cuenta de Ana. La coma de (valor,) crea una tupla de un solo elemento, que es lo que espera esta forma de pasar los parámetros.

No sustituyas los parámetros por la eliminación manual de comillas simples ni por el formateo de texto, por ejemplo mediante una f-string. Un nombre de usuario con una comilla simple puede ser un dato válido; la aplicación debe procesarlo de forma segura. La sintaxis de los parámetros depende de la biblioteca, por lo que no debes trasladar el patrón ? a cualquier controlador sin comprobar antes que lo admite.

Qué consecuencias puede tener la vulnerabilidad

SQL Injection puede permitir leer datos ajenos, eludir parte de los controles de acceso, modificar registros o eliminarlos. Las consecuencias reales dependen de la consulta, del motor de base de datos y de los permisos de la cuenta que utiliza la aplicación. Detectar esta vulnerabilidad no significa automáticamente que se pueda tomar el control de todo el servidor.

La ausencia de errores visibles tampoco demuestra que la aplicación sea segura. Esta puede ocultar los mensajes de la base de datos, y el cambio de comportamiento puede manifestarse únicamente como una diferencia en la respuesta. Por otra parte, un error aislado tras introducir una comilla simple es un indicio que conviene analizar, no una prueba suficiente de SQL Injection.

Qué revisar en tu aplicación

Empieza por los puntos donde se construyen las consultas SQL:

Para empezar, elige una búsqueda que utilice datos del usuario. Añade una prueba con un valor normal y otra con un texto que contenga una comilla simple. Comprueba que ambas entradas se siguen tratando como datos, sin modificar la estructura de la consulta.

Consulta también: ¿Qué es un ETF?

Lee también

Descubre cómo PHP-FPM atiende peticiones PHP, en qué se diferencia de un servidor web y cómo ajustar sus procesos al tráfico y los recursos.
Conoce BIND, el software abierto de servidor DNS: sus funciones, cómo gestiona dominios y cuándo conviene instalarlo por tu cuenta.
Descubre qué son el registro REGON de Polonia y su número, en qué se diferencian de NIP y KRS y cómo verificar los datos de una empresa.