flawopen.com/SQL Injection/IDs numéricos
¿Por qué el formato de cadenas es peligroso incluso para los IDs numéricos?
El riesgo no depende del significado real del valor: la consulta se construye concatenando texto. “Esto siempre es un número” es una suposición sobre los datos, no una garantía aplicada por el código.
Dónde falla la suposición
Un parámetro de URL o un campo de formulario viaja como texto, independientemente del tipo que espere la columna de la base de datos. Sin validación explícita, el framework no impide que alguien envíe id=1 OR 1=1 en vez de un número antes de que el código lo reciba.
Un código que hoy es seguro porque el ID siempre procede de una fuente interna confiable puede volverse vulnerable cuando otro llamador pasa un valor del usuario a la misma función. La vulnerabilidad ya estaba latente, esperando ese cambio.
Sobre todo en lenguajes de tipado dinámico, un valor que parece numérico en los registros puede seguir siendo una cadena con caracteres adicionales. Sin validación explícita, el tipo esperado no garantiza el tipo recibido.
Parametriza la consulta incluso después de validar el tipo
Valida el ID según el formato esperado por la aplicación y luego pásalo mediante la API de parámetros del controlador de base de datos. La validación protege el contrato de entrada; la parametrización evita que la entrada se interprete como sintaxis SQL. El comportamiento del controlador en el protocolo puede variar, así que no supongas que todos transmiten los valores del mismo modo. Los parámetros de PostgreSQL no representan identificadores de tablas o columnas; selecciona la estructura dinámica de la consulta de una lista fija permitida por la aplicación.
Vincula el valor numérico; valida su significado
Valida el ID como un entero positivo seguro si ese es el tipo que requiere la aplicación, pero aun así vincúlalo como valor de la consulta. La validación protege el contrato de entrada de la aplicación; la parametrización mantiene la entrada fuera de la sintaxis SQL. Los parámetros de PostgreSQL no sustituyen identificadores de tablas o columnas, así que elige cualquier estructura dinámica de consulta de una lista fija permitida por la aplicación.
const rawId = new URL(request.url).searchParams.get("id");
const result = await pool.query(
`SELECT id FROM users WHERE id = ${rawId}`
);
const rawId = new URL(request.url).searchParams.get("id");
if (rawId === null || !/^[0-9]+$/.test(rawId)) {
return new Response("Invalid id", { status: 400 });
}
const id = Number(rawId);
if (!Number.isSafeInteger(id) || id < 1) {
return new Response("Invalid id", { status: 400 });
}
const result = await pool.query(
"SELECT id FROM users WHERE id = $1",
Array.of(id)
);
Referencias
- flawopen.com — SQL Injection (todos los idiomas)
- ¿Qué es una consulta parametrizada?
- node-postgres — Parameterized query API