← Voltar ao blog

SQL Injection no Firebird: por que concatenar strings ainda derruba sistemas

Um parâmetro na URL e uma concatenação de string bastam para vazar dados no Firebird. Veja o ataque na prática e como o bind elimina a injeção.


Sistemas legados em PHP com Firebird compartilham um padrão perigoso: montar SQL juntando strings. Funciona por anos, até o dia em que um parâmetro vem da URL e o banco devolve dados que não deveria.

O problema não é a idade do código nem a falta de framework. É que a fronteira entre comando e dado desaparece quando você concatena. Neste artigo, o ataque é reproduzido, a causa é explicada e a correção é mostrada com o driver ibase.


O cenário real: um parâmetro na URL

Um endpoint público recebe um identificador na query string e busca o token correspondente:

$id = trim($_GET['identificador']);
$token = db_GetOne("SELECT token FROM WEBHOOKS_DETS WHERE identificador = '$id'");
echo $token;

A intenção é simples: buscar uma linha. O problema é que $id é texto livre. trim() remove espaços; não neutraliza SQL.

Com identificador=' OR 1=1 --, a consulta montada vira:

SELECT token FROM WEBHOOKS_DETS WHERE identificador = '' OR 1=1 --'

O OR 1=1 casa com a primeira linha da tabela e o -- comenta o resto. O atacante recebe um token que não era dele.

O que o atacante consegue fazer

Injeção não é só "ver um dado a mais". Dependendo do privilégio do usuário do banco, o impacto escala rápido:

Payload Efeito
' OR 1=1 -- retorna a primeira linha da tabela
x' UNION SELECT senha FROM USUARIOS -- lê outra tabela
x' AND (SELECT COUNT(*) FROM USUARIOS) > 0 -- inferência cega por tempo/resposta
'; UPDATE USUARIOS SET ATIVO = 'N' -- escrita indevida, quando o driver permite múltiplos comandos

No Firebird, o UNION é o caminho mais direto para vazar dados de outras tabelas. E endpoints públicos — webhooks, callbacks, captura de campanha — são a porta preferida porque não passam por sessão.

Por que trim e addslashes não resolvem

  • trim() remove espaços nas pontas. Aspas, -- e UNION continuam intactos.
  • addslashes() depende do conjunto de caracteres e do modo de escape do banco. Em ISO-8859-1 mal configurado, é possível contornar o escape.
  • magic_quotes foi removido do PHP há anos e nunca foi uma solução.
  • Validar o formato é bom, mas não é a causa raiz. Basta aparecer outro parâmetro, outro endpoint ou outro formato aceito.

A causa é a concatenação. A correção precisa acontecer no ponto onde o SQL é montado.

A solução: bind de parâmetros

O driver ibase suporta parâmetros posicionais com ?. O SQL e os valores viajam separados; o banco trata o valor como dado, nunca como comando.

Com um wrapper comum em sistemas PHP/Firebird:

$token = db_GetOne(
    'SELECT token FROM WEBHOOKS_DETS WHERE identificador = ?',
    [$id]
);

Com ibase puro:

$stmt = ibase_prepare($conn, 'SELECT token FROM WEBHOOKS_DETS WHERE identificador = ?');
$res  = ibase_execute($stmt, $id);
$token = $res ? ibase_fetch_row($res)[0] : '';

Ou, quando o wrapper aceita parâmetros inline:

$res = ibase_query($conn, 'SELECT token FROM WEBHOOKS_DETS WHERE identificador = ?', $id);

O driver envia a query e o array de valores em chamadas distintas (ibase_query($conn, $sql, ...$params)). Por isso ' OR 1=1 -- deixa de ser sintaxe e passa a ser apenas uma string procurada na coluna, que não existe.

Antes e depois

Entrada Antes Depois
identificador válido token correto token correto
zzz' OR '1'='1 token de outra linha vazio
x' erro de SQL do Firebird vazio, sem erro
parâmetro ausente warning no PHP vazio, resposta 200
identificador[]=a warning de array vazio, resposta 200

Note a última linha: além da injeção, o bind com validação de tipo evita avisos que revelam detalhes internos.

Defesa em profundidade

Bind resolve a injeção, mas algumas camadas extras custam pouco:

  1. Valide o formato esperado. Um identificador numérico pode ser checado antes de consultar:

    if (!preg_match('/^\d{5,25}$/', $id)) {
        echo '';
        exit;
    }
    
  2. Não exiba erro de SQL para o usuário. Em produção, DEBUG ligado transforma um erro em mapa do banco. Logue internamente e devolva resposta genérica.

  3. Aplique o bind na escrita também. UPDATE, DELETE e INSERT concatenados têm o mesmo problema, com dano maior.

  4. Use o menor privilégio possível. Um endpoint de leitura não precisa de permissão para alterar tabelas, criar objetos ou ler outras tabelas.

  5. Revise os pontos sem login. Webhooks, callbacks e endpoints de campanha são a porta de entrada preferida porque não passam por sessão.

  6. Trate o parâmetro como ausente. $_GET['x'] sem isset gera warning e pode virar string vazia silenciosa. Use isset + is_string.

  7. Teste com cargas maliciosas. Aspas, --, UNION, \0, arrays e valores vazios. O teste feliz não prova nada.

Como caçar injeção no código

Procure os padrões que denunciam concatenação:

"SELECT ... '$var'"
"WHERE ... $var"
"... = $var"
"ORDER BY $var"
"LIMIT $var"

Os dois últimos são fáceis de esquecer: ORDER BY e paginação também recebem dados externos e não aceitam bind em todos os casos. Para esses, use uma lista branca de colunas e direções permitidas.

$colunas = ['nome', 'data', 'valor'];
$ordem = in_array($_GET['ordem'] ?? '', $colunas, true) ? $_GET['ordem'] : 'data';
$dir   = ($_GET['dir'] ?? 'asc') === 'desc' ? 'DESC' : 'ASC';
$sql   = "SELECT * FROM CLIENTES ORDER BY $ordem $dir";

Aqui a variável interpolada é segura porque só pode assumir valores de uma lista fixa.

Passo a passo para corrigir um endpoint

  1. Reproduza com um payload que comprove o vazamento ou o erro.
  2. Localize a montagem da string e identifique todos os parâmetros externos.
  3. Troque por ? com array de parâmetros.
  4. Valide o formato quando ele é conhecido.
  5. Teste entrada válida, inválida, ausente, array e payloads clássicos.
  6. Confira os logs para garantir que não sobrou erro de SQL exposto.
  7. Monitore após o deploy, especialmente se o endpoint for público.

Conclusão

SQL injection no Firebird não é um problema de banco exótico: é o mesmo erro de sempre, concatenar dado externo em comando. O bind elimina a raiz, a validação de formato reduz a superfície e o menor privilégio limita o estrago se algo passar.

O Sqlify gera SQL Firebird a partir de português e usa parâmetros posicionais nos exemplos, para você começar com o pé direito. Descreva a consulta em português e receba a query pronta, com a explicação de cada parte.

Leia também: