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
| Palabra | Qué significa | Ejemplo |
|---|---|---|
| Consulta | orden que pide datos a una base de datos | SELECT title FROM books |
| Inyección SQL | texto de un usuario que cambia la orden | |
| Parámetro | valor enviado aparte de la orden | ? |
| Consulta parametrizada | orden con huecos donde van los valores | |
| Escapar | marcar 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
# 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 escribe | La orden que recibe la base | Qué 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
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
docker exec app grep -c "VALUES (?, ?" /tmp/app/app.pySe 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
docker exec app grep -c "LIKE" /tmp/app/app.pySe 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
docker exec app grep -c "sqlite3.Error" /tmp/app/app.pySe 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
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")