O que é SQL Injection? Como funciona e como evitar

O que é SQL Injection? Como funciona e como evitar

Você digita um nome de usuário numa tela de login e, em vez de um nome, coloca um pedaço de código. O site não reclama: ele executa. É assim, em essência, que funciona o SQL Injection, uma falha descrita publicamente há mais de 25 anos e que continua aparecendo em incidentes de grande porte. Este post explica o mecanismo com um exemplo que dá para entender sem ser programador e mostra o que realmente resolve o problema.

O que é SQL Injection?

O que é SQL Injection em uma frase?

SQL Injection é um ataque em que o invasor insere comandos SQL num campo de entrada (formulário, URL, cookie) e a aplicação os executa no banco de dados como se fossem parte da consulta legítima.

SQL é a linguagem que aplicações usam para conversar com bancos de dados como MySQL, PostgreSQL e SQL Server. O problema nasce quando o programador monta a consulta juntando texto fixo com o que o usuário digitou. A partir daí, o banco não consegue distinguir o que é comando do programador e o que é dado do usuário.

Como o ataque funciona, passo a passo

Imagine um login que roda esta consulta, montada por concatenação de texto:

SELECT * FROM usuarios
WHERE login = 'INPUT_LOGIN' AND senha = 'INPUT_SENHA';

Se o invasor digitar admin'-- no campo de login, a consulta vira:

SELECT * FROM usuarios
WHERE login = 'admin'--' AND senha = '';

Em SQL, -- inicia um comentário. Tudo depois dele é ignorado, inclusive a verificação da senha. O banco devolve o usuário admin e a aplicação o deixa entrar. Nenhuma senha foi quebrada, e nenhum malware foi instalado: o próprio sistema fez o trabalho.

O que um atacante consegue fazer além de burlar o login?

Dependendo das permissões do banco, ele pode ler tabelas inteiras (usuários, e-mails, hashes de senha, cartões), alterar ou apagar dados e, em configurações ruins, chegar a executar comandos no servidor. As variações mais comuns são:

  • In-band (clássico): o resultado da consulta injetada aparece na própria resposta da página, usando UNION SELECT para puxar dados de outras tabelas.
  • Baseado em erro: mensagens de erro do banco vazam nomes de tabelas e colunas.
  • Blind (às cegas): a página não mostra dados, mas o atacante deduz informação pelo comportamento (resposta verdadeira ou falsa, tempo de resposta) e extrai o conteúdo bit a bit, geralmente com ferramentas automatizadas como o sqlmap.

A descoberta de alvos também é barata. Técnicas de busca como as explicadas em Google Dorks: como hackers usam o Google para achar dados expostos ajudam a listar páginas com parâmetros na URL, candidatas a teste.

Casos reais: o ataque não é coisa do passado

  • Heartland, Hannaford e 7-Eleven (2008–2009): o Departamento de Justiça dos EUA indiciou Albert Gonzalez e outros em agosto de 2009. Segundo a acusação, o grupo usou SQL injection para instalar sniffers nos servidores das empresas e interceptar dados de cartão, com mais de 130 milhões de números de cartão de crédito e débito roubados. A CSO Online e a Computerworld registraram o caso na época.
  • Sony (2011): o grupo LulzSec afirmou ter acessado dados da Sony Online Entertainment por SQL injection, como relatamos em PSN fora do ar: o hack que expôs 77 milhões de contas em 2011.
  • MOVEit Transfer (2023): a CVE-2023-34362 era uma falha de SQL injection explorada em massa e virou um dos maiores ataques de cadeia de suprimentos da década, detalhado em MOVEit: 3 anos do zero-day que expôs 93 milhões de registros.

A injeção de código ficou na categoria "Injection" do OWASP Top 10 (A03:2021), um dos riscos mais conhecidos de aplicações web.

Como evitar SQL Injection

Qual é a defesa mais eficaz contra SQL Injection?

Usar consultas parametrizadas (prepared statements), que mantêm o código SQL e os dados do usuário separados, de modo que a entrada nunca pode alterar a estrutura da consulta. É a primeira defesa recomendada pelo SQL Injection Prevention Cheat Sheet da OWASP.

Exemplo em Python com sqlite3 (o mesmo princípio vale em PHP/PDO, Java/JDBC, C# e Node):

# Vulnerável: concatenação
cur.execute("SELECT * FROM usuarios WHERE login = '" + login + "'")

# Seguro: parâmetro
cur.execute("SELECT * FROM usuarios WHERE login = ?", (login,))

Com o parâmetro, admin'-- é tratado como um nome de usuário esquisito, não como SQL.

Outras camadas, em ordem de importância:

  1. Menor privilégio no banco: a conta da aplicação não deve ser dona do banco nem ter permissão de administrador. Se o ataque passar, o estrago é limitado.
  2. Validação por lista permitida: para campos que não aceitam parâmetro (nome de coluna, ordenação), aceite só valores pré-definidos. A OWASP desencoraja confiar apenas em escape de caracteres.
  3. Esconder mensagens de erro detalhadas do usuário final; registre no log, não na tela.
  4. WAF como complemento: ajuda a barrar padrões conhecidos, mas não substitui corrigir o código.
  5. Atualizar software de terceiros: no caso do MOVEit, o código vulnerável não era da vítima. Patch rápido e inventário de sistemas expostos importam tanto quanto o código próprio.
  6. Hash de senhas forte: se a tabela vazar, o dano cai muito. Veja também o caso Adobe 2013 e o que não fazer com senhas.

E se você não é desenvolvedor?

Você não corrige o código do site, mas controla o estrago quando ele vaza: use senha única por serviço num gerenciador (como usar o Bitwarden), ative 2FA e confira se seu e-mail apareceu em vazamentos. Se você mantém um site (WordPress, loja, sistema interno), mantenha plugins e temas atualizados, porque a maioria das falhas de SQL injection em sites pequenos vem de plugins desatualizados.

A lição maior

SQL Injection é um erro de design: misturar comando e dado no mesmo texto. A correção também é de design, e não depende de filtro mágico. Se quiser seguir o fio dos ataques a credenciais depois de um vazamento, leia Credential Stuffing: o que é e como funciona.

Comentários