flawopen.com/SQL Injection/IDs numéricos
Por que a formatação de strings é perigosa até para IDs numéricos?
O risco não depende do significado real do valor: a consulta é construída juntando texto. “Isso sempre é um número” é uma suposição sobre os dados, não uma garantia imposta pelo código.
Quando a suposição falha
Um parâmetro de URL ou campo de formulário é texto na transmissão, seja qual for o tipo esperado pela coluna do banco. Sem validação explícita, o framework não impede que alguém envie id=1 OR 1=1 em vez de um número antes de o código receber o valor.
Um código hoje seguro porque o ID vem de uma fonte interna confiável pode ficar vulnerável quando outro chamador passa um valor do usuário à mesma função. A falha já estava latente, à espera dessa mudança.
Em especial nas linguagens de tipagem dinâmica, um valor que parece numérico nos logs ainda pode ser uma string com caracteres adicionais. Sem validação explícita, o tipo esperado não é necessariamente o tipo recebido.
Parametrize a consulta mesmo após validar o tipo
Valide o ID conforme o formato esperado pela aplicação e, em seguida, passe-o pela API de parâmetros do driver do banco de dados. A validação protege o contrato de entrada; a parametrização impede que a entrada seja interpretada como sintaxe SQL. O comportamento do driver no protocolo pode variar, portanto não presuma que todos transmitem valores da mesma forma. Parâmetros do PostgreSQL não representam identificadores de tabela ou coluna; escolha estruturas dinâmicas da consulta em uma lista fixa permitida pela aplicação.
Vincule o valor numérico; valide seu significado
Valide o ID como um inteiro positivo seguro quando esse for o tipo exigido pela aplicação, mas ainda o vincule como valor da consulta. A validação protege o contrato de entrada da aplicação; a parametrização mantém a entrada fora da sintaxe SQL. Parâmetros do PostgreSQL não substituem identificadores de tabela ou coluna, então escolha qualquer estrutura dinâmica da consulta em uma lista fixa permitida pela aplicação.
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)
);
Referências
- flawopen.com — SQL Injection (todos os idiomas)
- O que é uma consulta parametrizada?
- node-postgres — Parameterized query API