SQL Injection em 2026: por que prepared statements continuam sendo a resposta

A SQL Injection foi descrita publicamente em 1998, e a correção circula desde então. Prepared statements, também chamados de consultas parametrizadas, enviam o comando e os dados ao banco em etapas separadas. Com isso, o que o usuário digita nunca vira parte do comando. 

Mesmo assim, a falha continua aparecendo em código novo. O motivo costuma ser o mesmo: a equipe confia na sanitização de entrada, no WAF ou no ORM e mantém a concatenação de strings em algum ponto do sistema. Os três reduzem a exposição. A causa permanece, e ela é uma consulta montada com dado externo e entregue ao banco como se fosse código.

Por que uma falha de 1998 continua no topo das listas 

A injeção segue como categoria do OWASP Top 10, e a SQL Injection tem identificador próprio no catálogo de fraquezas de software, a CWE-89. Três fatores explicam a permanência. 

  • Concatenar é o caminho mais curto. Montar a consulta com + ou interpolação funciona no primeiro teste e passa em todo caso de uso legítimo. O defeito só aparece com entrada hostil. 
  • O código antigo continua em produção. Sistemas escritos antes de a equipe adotar um padrão de acesso a dados raramente são reescritos. Eles seguem expostos por APIs novas. 
  • A consulta dinâmica foge do padrão. Relatórios com filtros opcionais, ordenação escolhida pelo usuário e buscas avançadas levam o desenvolvedor de volta à montagem manual de SQL, mesmo em projeto que usa ORM. 

Do lado do atacante, o custo de explorar é baixo. Das 635 vulnerabilidades sob exploração ativa em 2025, 53,86% tinham prova de conceito pública. O número cobre todas as classes de falha, e a SQL Injection é só uma delas. Ele mostra o padrão: em mais da metade dos casos, o atacante parte de um roteiro pronto. 

Na SQL Injection, esse roteiro é ainda mais acessível. A técnica está documentada, e ferramentas abertas como o sqlmap testam e exploram um parâmetro vulnerável de forma automática. 

Para quem responde por risco, a leitura é direta. A falha é barata de explorar e barata de evitar no momento em que o código é escrito. O custo sobe quando ela é descoberta em produção. 

O que realmente acontece: dado sendo interpretado como comando 

O banco de dados recebe a consulta como texto e a analisa para decidir o que executar. Quando a aplicação monta esse texto juntando SQL fixo com entrada do usuário, o banco não tem como saber onde termina um e começa o outro. 

Considere uma tela de login que monta a consulta assim: 

query = (
    "SELECT id, nome FROM usuarios "
    "WHERE email = '" + email + "' AND senha_hash = '" + senha_hash + "'"
)
cursor.execute(query)

Com um e-mail comum, o resultado é o esperado. Se o atacante digita ana@empresa.com‘ — no campo de e-mail, o banco recebe: 

SELECT id, nome FROM usuarios
WHERE email = 'ana@empresa.com' -- ' AND senha_hash = '...'

A aspa simples fechou o literal antes da hora. O — abriu um comentário e descartou a verificação de senha. O atacante entra como Ana sem conhecer a senha dela.

O mesmo mecanismo vai além do login. Com UNION SELECT, o atacante lê outras tabelas. Com consultas empilhadas, onde o driver permite, ele altera ou apaga dados. O alcance depende das permissões da conta que a aplicação usa para se conectar ao banco. 

A definição da falha cabe em uma linha: um dado foi interpretado como comando. A defesa que resolve atua exatamente nesse ponto.

Por que lista de bloqueio e escape manual falham 

As duas abordagens mantêm a concatenação e tentam tornar o dado inofensivo antes de juntá-lo ao SQL. Ambas dependem de prever como o analisador do banco vai ler a string final. 

Lista de bloqueio 

Um filtro que rejeita ou remove SELECT, UNION, — e aspas cai diante de variações que o banco aceita e o filtro não previu. 

Filtro aplicado  Entrada que passa  Por que passa 
Bloqueia UNION SELECT em maiúsculas  uNiOn sElEcT  Palavras-chave de SQL não diferenciam maiúsculas de minúsculas 
Bloqueia UNION SELECT com espaço  UNION/**/SELECT  Um comentário vazio funciona como separador 
Remove a palavra SELECT uma vez  SELSELECTECT  A remoção do trecho central reconstrói a palavra 
Bloqueia aspas simples  1 OR 1=1 em campo numérico  Campo numérico dispensa aspas 

A lista também gera falso positivo. Sobrenomes como O’Connor e D’Ávila contêm aspa simples. Um campo de comentário pode conter a palavra “select”. Bloquear essas entradas quebra o uso legítimo e deixa a falha aberta. 

Escape manual 

Escapar aspas antes de concatenar resolve o caso mais simples e deixa outros abertos. 

  • Contexto numérico. Em “WHERE id = ” + id, não há aspa para escapar. A entrada 1 OR 1=1 devolve a tabela inteira. 
  • Conjunto de caracteres. Em algumas codificações multibyte, o byte da barra de escape é absorvido pelo caractere anterior, e a aspa volta a valer como delimitador. 
  • Segunda ordem. O dado é escapado na gravação e armazenado em sua forma original. Mais tarde, outro trecho de código o concatena em uma nova consulta, tratando-o como confiável porque veio do próprio banco. 
  • Identificadores. Nome de coluna em ORDER BY não fica entre aspas de literal. A função de escape de strings não se aplica. 

O guia de prevenção de SQL Injection da OWASP desaconselha o escape de entrada como defesa. Validar o formato continua valendo como higiene: e-mail com estrutura de e-mail, identificador numérico só com dígitos. Isso reduz a superfície, mas a consulta ainda precisa ser parametrizada. 

Consultas parametrizadas: separar código de dado, na origem 

A consulta parametrizada, ou prepared statement, é a defesa primária contra SQL Injection. Ela muda a forma como a consulta chega ao banco. Primeiro vai o comando, com marcadores no lugar dos valores, e o banco analisa esse texto e fixa a estrutura. Depois vão os valores, que só podem ocupar o lugar de valores. 

Em Python, com o driver do banco: 

cursor.execute(
    "SELECT id, nome FROM usuarios WHERE email = %s AND senha_hash = %s",
    (email, senha_hash),
)

Em Java, com JDBC:

PreparedStatement ps = conn.prepareStatement(
    "SELECT id, nome FROM usuarios WHERE email = ? AND senha_hash = ?");
ps.setString(1, email);
ps.setString(2, senhaHash);
ResultSet rs = ps.executeQuery();

Com a entrada ana@empresa.com' -- , o banco procura um usuário cujo e-mail seja literalmente esse texto, com aspa e traços. Não encontra, e o login falha. A aspa perdeu o significado sintático porque a análise do comando terminou antes de o dado chegar.

Um detalhe do exemplo em Python merece atenção. Os valores vão como segundo argumento de execute. Escrever "... email = '%s'" % email usa a formatação de strings da linguagem e devolve o código à concatenação.

O que o marcador não cobre

Marcadores substituem valores. Nome de tabela, nome de coluna, direção de ordenação e palavras-chave não podem ser parametrizados. Para esses casos, a aplicação escolhe entre opções fixas no código:

COLUNAS_ORDENAVEIS = {"nome": "nome", "data": "criado_em"}
coluna = COLUNAS_ORDENAVEIS.get(parametro_ordem, "nome")
query = f"SELECT id, nome FROM clientes ORDER BY {coluna}"

O usuário escolhe uma chave. O texto que entra no SQL sai do dicionário, escrito pelo desenvolvedor.

Outros dois cuidados:

  • Stored procedure só protege se não montar SQL dinâmico por concatenação dentro dela.
  • Cláusula IN com lista variável pede um marcador por item, gerado pelo código. Juntar os valores com vírgula em uma string reabre a falha.

Onde o ORM ainda deixa passar

ORMs geram consultas parametrizadas por padrão. Enquanto o código usa a API de objetos, a proteção vem junto. A falha volta nos pontos em que o framework aceita SQL escrito à mão.

Raw query. Todo ORM tem uma saída para SQL direto: raw() e extra() no Django, text() no SQLAlchemy, createNativeQuery() no Hibernate, FromSqlRaw() no Entity Framework, sequelize.query() no Sequelize. Essas funções aceitam parâmetros, mas também aceitam uma string já montada.

# Vulnerável: interpolação dentro de raw()
Cliente.objects.raw(f"SELECT * FROM clientes WHERE nome = '{nome}'")

# Correto: valor passado como parâmetro
Cliente.objects.raw("SELECT * FROM clientes WHERE nome = %s", [nome])

Concatenação dinâmica na linguagem do ORM. HQL e JPQL também passam por um analisador. Montar "FROM Pedido WHERE status = '" + status + "'" cria a mesma falha uma camada acima.

Fragmentos em query builders. Métodos como whereRaw(), orderByRaw() e literal() inserem texto direto no SQL gerado. Eles costumam aparecer em filtros opcionais e em ordenação configurável.

Em uma reunião de risco, “usamos ORM” descreve o padrão do projeto e diz pouco sobre as exceções. A informação que falta é quantas raw queries existem no repositório e quem revisa cada uma.

WAF como camada, não como correção

O WAF inspeciona a requisição HTTP e a compara com assinaturas e padrões de ataque. Ele não vê como a aplicação monta a consulta. Por isso, herda as limitações de uma lista de bloqueio:

  • Variações de codificação, comentários e sintaxe menos comum do banco podem não casar com nenhuma assinatura.
  • Tráfego que não passa por ele fica sem inspeção: chamadas entre serviços internos, jobs em lote, integrações diretas com parceiros.
  • A injeção de segunda ordem dispara em uma consulta posterior, quando a requisição do momento não carrega payload algum.

Ainda assim, o WAF tem três usos legítimos nesse cenário. Ele serve como correção virtual enquanto o ajuste no código passa por teste e deploy. Reduz o volume de varredura automatizada que chega à aplicação. Além disso, gera telemetria para o SOC sobre quem está testando quais parâmetros.

O cuidado está no registro. Uma regra de WAF que bloqueia a exploração deixa a vulnerabilidade no código. O item continua aberto no backlog, com dono e prazo, até a consulta ser parametrizada.

A mesma lógica vale para a outra camada que limita o dano: a conta de banco da aplicação. Ela deve operar com o menor privilégio possível, sem permissão de DDL e sem acesso a esquemas que a aplicação não usa.

Checklist de code review para AppSec

A lista abaixo cabe em um template de pull request. Cada item tem resposta de sim ou não.

  • Nenhuma consulta monta SQL com concatenação, interpolação ou formatação de string a partir de dado externo. Busque +, f-string, String.format e ${} perto de SELECT, INSERT, UPDATE, DELETE e WHERE.
  • Todo valor chega ao banco por marcador. O driver recebe o comando e os parâmetros em argumentos separados.
  • Toda raw query do ORM está listada e justificada. Busque raw(, text(, FromSqlRaw, createNativeQuery, whereRaw, orderByRaw e literal(.
  • Nome de tabela, nome de coluna e direção de ordenação saem de uma lista de permissão fixa no código.
  • Consultas em HQL ou JPQL usam parâmetros nomeados, sem concatenação.
  • Nenhuma stored procedure monta SQL dinâmico por concatenação.
  • Dado lido do banco é tratado como não confiável quando volta para outra consulta.
  • Cláusulas IN com lista variável geram um marcador por item.
  • A conta de banco da aplicação não tem permissão de DDL nem acesso a esquemas de outros sistemas.
  • Mensagens de erro do banco não chegam ao cliente.
  • O pipeline tem regra de análise estática para SQL concatenado, e ela bloqueia o merge.
  • Toda regra de WAF usada como correção virtual tem um ticket de correção no código, com dono e prazo.

Na prática

Sanitização, WAF e ORM diminuem a chance de exploração. A consulta parametrizada remove a condição que torna a exploração possível. Em qualquer revisão de código que toque o banco, a verificação é uma só: se algum dado externo chega ao SQL por um caminho que não seja um parâmetro.

Participe do CyberSprint

Secure coding é um dos temas do CyberSprint, o game que nós, da Fast Lane, promovemos no mês da cibersegurança. Participe, responda uma pergunta por dia e concorra a um drone DJI Mini 4K. A pontuação combina acerto e tempo de resposta.

[INSERIR LINK DE CADASTRO DO CYBERSPRINT]

Fonte: Relatório do Cenário Global de Ameaças 2026, FortiGuard Labs / Fortinet, 2026.

Cyber Security, DevOps, Fast Lane, Uncategorized

A Fast Lane é uma empresa global premiada, especializada em treinamentos em tecnologia e negócios, além de oferecer serviços de consultoria para transformação digital. Como único parceiro global dos três principais provedores de nuvem — Microsoft, AWS e Google — e parceiro de outros 30 importantes fornecedores de TI, incluindo Cisco, Aruba, VMware, Palo Alto Networks, Red Hat, entre outros, a Fast Lane oferece soluções de qualificação e serviços profissionais que podem ser escalados conforme a necessidade. Mais de 4.000 profissionais experientes da Fast Lane treinam e orientam clientes de organizações de todos os tamanhos em 90 países ao redor do mundo, nas áreas de nuvem, inteligência artificial, cibersegurança, desenvolvimento de software, redes sem fio e mobilidade, ambiente de trabalho moderno, bem como gestão de TI e projetos.

Mais artigos sobre o tema
Cyber Security
Descubra todos os treinamentos e certificações em Cyber Security que Fast Lane oferece.

Calendário de treinamentos Fast Lane

Quer saber quais treinamentos vão acontecer em breve? Consulte nosso calendário e adquira os conhecimentos com nossos experts.