¿Te resulta más fácil leer con fondo crema, un tono de color, más contraste o un texto más grande? Elige tus colores y tu texto una sola vez, para todas las páginas.

Lección gratuita

Evitar la inyección SQL con parámetros

Después de esta lección: Puedo escribir una consulta con parámetros para que el texto del usuario no cambie la orden.

Lección de 60 min3 comandosLaboratorio de inyección SQL: ataca una búsqueda débil, corrige la consulta y demuéstralo

Una consulta con parámetros no se deja cambiar

Una consulta con parámetros es una consulta SQL donde los valores viajan aparte del texto de la orden. Así lo que escribe un usuario nunca puede cambiar la orden.

Piensa en el catálogo de una biblioteca universitaria. Si el buscador pega el título dentro de la orden, un título raro puede cambiarla.

Al final de esta lección

Nota

Puedo escribir una consulta con parámetros para que el texto del usuario no cambie la orden.

Palabras clave

PalabraQué significaEjemplo
Consultaorden que pide datos a una base de datosSELECT title FROM books
Inyección SQLtexto de un usuario que cambia la orden
Parámetrovalor enviado aparte de la orden?
Consulta parametrizadaorden con huecos donde van los valores
Escaparmarcar un carácter para que no cuente como orden

Las celdas que rellenan los estudiantes quedan en blanco en esta vista previa.

Pegar texto cambia la orden

Un programa ingenuo monta la consulta sumando trozos de texto. La base de datos recibe una sola cadena y no sabe qué parte puso el programador y cuál el usuario.

Si el usuario escribe una comilla, puede cerrar el texto del programador y añadir sus propias condiciones.

La orden que monta un programa ingenuo

Código: python
# Una biblioteca inventada. Solo se imprime la orden, no se ejecuta.
title = "Dune"
order = "SELECT title, author FROM books WHERE on_loan = 0 AND title = '" + title + "'"
print(order)
# Cambia title por "x' OR '1'='1" y pulsa Run. Mira cómo cambia la orden.

Ejemplo resuelto: dos títulos, dos órdenes

El usuario escribeLa orden que recibe la baseQué ocurre
Dune... title = 'Dune'Busca el libro Dune
x' OR '1'='1... title = 'x' OR '1'='1'La condición se cumple siempre: devuelve todos los libros
O'Brien... author = 'O'Brien'La comilla rompe la orden y da un error

Las celdas que rellenan los estudiantes quedan en blanco en esta vista previa.

Un parámetro envía el valor aparte

En una consulta parametrizada la orden lleva un ? donde irá cada valor. Los valores se envían en una lista aparte.

La base de datos lee primero la orden y fija su forma. Después coloca los valores, que solo pueden ser datos. Ninguna comilla cierra nada.

La misma búsqueda con un parámetro

Código: python
import sqlite3
db = sqlite3.connect(":memory:")
db.execute("CREATE TABLE books (title TEXT, author TEXT, on_loan INTEGER)")
db.execute("INSERT INTO books VALUES (?, ?, ?)", ("Dune", "Herbert", 0))
db.execute("INSERT INTO books VALUES (?, ?, ?)", ("Ficciones", "Borges", 0))
# Cambia title por "x' OR '1'='1" y pulsa Run: no devuelve nada.
title = "Dune"
rows = db.execute("SELECT title, author FROM books WHERE on_loan = 0 AND title = ?", (title,)).fetchall()
print(rows)

Mira cómo está escrita la tienda

El programa de la tienda de tu laboratorio ya usa parámetros en unos sitios y no en otros. Ejecuta estos tres comandos. Solo cuentan líneas del código original: no te enseñan la línea que falla ni la arreglan.

La regla única

Advertencia

Ataca solo tu copia privada del laboratorio, que no tiene salida a internet. Probar un sistema ajeno, o sin permiso, es delito en casi todos los países.

Cuántas líneas usan huecos

Comando
docker exec app grep -c "VALUES (?, ?" /tmp/app/app.py

Se muestra aquí y no se ejecuta. Crea una cuenta gratuita para pulsar Ejecutar y escribirlo en el terminal de tu propio laboratorio.

Cuántas líneas buscan con LIKE

Comando
docker exec app grep -c "LIKE" /tmp/app/app.py

Se muestra aquí y no se ejecuta. Crea una cuenta gratuita para pulsar Ejecutar y escribirlo en el terminal de tu propio laboratorio.

Cuántas líneas capturan un error de la base

Comando
docker exec app grep -c "sqlite3.Error" /tmp/app/app.py

Se muestra aquí y no se ejecuta. Crea una cuenta gratuita para pulsar Ejecutar y escribirlo en el terminal de tu propio laboratorio.

Qué te dicen las tres respuestas

El primero imprime 2: dos órdenes de la tienda ya usan huecos ?. El segundo imprime 1: una sola línea hace la búsqueda con LIKE. El tercero imprime 1: hay un sitio que atrapa el error de la base.

Compara la forma de esas dos órdenes con la de la búsqueda. Esa diferencia es lo que arregla el laboratorio.

Un error frecuente

Advertencia

Mucha gente cree que escapar las comillas es igual de seguro que un parámetro. No es así. Escapar a mano deja casos olvidados, como otra codificación o un número sin comillas. El parámetro separa orden y dato siempre, así que no depende de acordarte de nada.

Un nombre de columna no puede ser parámetro

Un parámetro solo puede ser un valor. No puede ser el nombre de una tabla, de una columna ni un orden.

Si una página deja elegir por qué columna ordenar, la palabra se escribe en la orden. Antes hay que compararla con una lista de nombres permitidos.

Elegir una columna solo de una lista

Código: python
allowed = ["title", "author", "year"]
# Cambia choice por "year" y luego por "password", y pulsa Run.
choice = "title"
if choice in allowed:
    print("SELECT title FROM books ORDER BY " + choice)
else:
    print("columna no permitida")

Sobre este laboratorio y su código: Laboratorio de inyección SQL: ataca una búsqueda débil, corrige la consulta y demuéstralo

¿Enseñas a una clase? Asigna este módulo a tus alumnos con guía del profesor, tareas con corrección automática y un laboratorio para cada alumno.