Pular para o conteúdo
Categoria: Pentest & Hacking Ético12 min de leitura

Explorando SQL Injection na prática (ético)

Por Lucas Andrade ·

Aprenda a identificar e explorar SQL Injection em laboratório, entender o impacto real da falha e por que consultas parametrizadas são a defesa definitiva.

Explorando SQL Injection na prática (ético)

SQL Injection (SQLi) é uma das falhas mais antigas e ainda mais perigosas da web. Ela acontece quando a entrada do usuário é concatenada diretamente numa consulta SQL, permitindo que um atacante altere a lógica da query. Neste artigo, vamos explorar a SQLi do ponto de vista do pentester — sempre em ambiente de laboratório autorizado.

O objetivo aqui não é apenas atacar, mas entender o mecanismo a fundo, para defender melhor. Se você ainda não conhece o conceito básico, comece por O que é SQL Injection e como se proteger. Este guia foca na exploração prática dentro da Metodologia de pentest: as fases de um teste de invasão.

Para o desenvolvedor que constrói apps com IA, há um motivo extra de urgência: assistentes de código frequentemente sugerem queries por concatenação de strings, reproduzindo o antipadrão vulnerável. Saber reconhecer e explorar SQLi é o que te permite revisar criticamente o código gerado — seu e da máquina — em vez de confiar cegamente.

Aviso ético e ambiente seguro

Antes de qualquer comando: explorar SQLi em sistemas sem autorização é crime. Monte um laboratório legal para praticar:

  • Aplicações deliberadamente vulneráveis (DVWA, bWAPP, OWASP Juice Shop).
  • Máquinas virtuais isoladas, sem acesso à internet de produção.
  • Desafios de O que é um CTF? Aprenda hacking jogando.

Todo exemplo deste artigo pressupõe um alvo de treino que você controla. Em um engajamento profissional, a autorização vem por escrito, com escopo definido — quais alvos, quais técnicas, quais janelas de horário. Sem esse documento, mesmo um teste "inofensivo" pode configurar acesso não autorizado. O pentester ético trata a autorização como o primeiro artefato do trabalho, não como detalhe burocrático.

Por que a SQLi acontece

A causa raiz é simples: a aplicação mistura código e dados. Considere uma consulta de login montada por concatenação:

# VULNERÁVEL — nunca faça isso
query = "SELECT * FROM usuarios WHERE email = '" + email + "' AND senha = '" + senha + "'"

Se o atacante envia no campo email:

' OR '1'='1

A query final vira:

SELECT * FROM usuarios WHERE email = '' OR '1'='1' AND senha = '...'

A condição '1'='1' é sempre verdadeira, quebrando a lógica de autenticação. O estudo seminal de Halfond, Viegas e Orso classifica esse tipo de manipulação de lógica como uma das categorias fundamentais de SQLi (Halfond, Viegas & Orso, 2006).

O ponto conceitual a internalizar: para o banco de dados, não existe "campo de email" — existe apenas uma string de SQL que ele interpreta. Quando você concatena entrada do usuário nessa string, você transfere ao usuário o poder de reescrever o comando. A aspa simples é a chave que abre essa porta, porque é o caractere que encerra um literal de texto e devolve o controle para a sintaxe SQL.

Tipos de SQL Injection

Conhecer os tipos orienta a estratégia de exploração:

  • In-band (clássica): o resultado volta na própria resposta. Inclui error-based (vaza dados via mensagens de erro) e UNION-based (usa UNION para anexar dados).
  • Blind (cega): a aplicação não retorna dados nem erros visíveis, mas o comportamento muda. Subdivide-se em boolean-based (resposta verdadeira/falsa) e time-based (atrasos induzidos).
  • Out-of-band: o dado sai por um canal alternativo (uma requisição DNS ou HTTP disparada pelo banco).

A escolha do tipo depende do que o alvo revela. O Manual do Web Application Hacker descreve essa progressão do mais informativo (in-band) ao mais sutil (blind) (Stuttard & Pinto, 2011).

Na prática, a árvore de decisão do pentester é: o erro do banco aparece na resposta? Se sim, error-based é o caminho mais rápido. O conteúdo da consulta é exibido na página? Se sim, UNION-based extrai dados em volume. Nada disso, mas a página muda de comportamento? Então é blind boolean. Nem o comportamento muda visivelmente? Resta o blind time-based ou o out-of-band.

Onde a injeção se esconde

Iniciantes testam só campos de formulário, mas SQLi pode estar em qualquer entrada que chegue a uma query:

  • Parâmetros de URL (?id=10, ?categoria=livros).
  • Campos de formulário (login, busca, filtros).
  • Cabeçalhos HTTP (User-Agent, Referer, Cookie) — quando logados em alguma tabela.
  • Corpo JSON de APIs, que muitas vezes vira query no backend.
  • Parâmetros usados em ORDER BY ou nomes de coluna, onde a parametrização tradicional não se aplica e o risco é maior.

Mapear todas as superfícies de entrada faz parte da fase de reconhecimento da metodologia de pentest. Cada parâmetro é um candidato a teste.

Passo 1: detectar a injeção

A primeira pista é provocar uma quebra de sintaxe. Insira uma aspa simples num parâmetro e observe:

https://loja.exemplo.com/produto?id=10'

Sinais de injeção:

  • Erro de banco de dados na resposta.
  • Página em branco ou diferente.
  • Mudança no tempo de resposta.

Um proxy facilita muito esse teste, pois permite reenviar e variar a requisição rapidamente. Veja como no guia Burp Suite: o canivete suíço do pentester web. Comparar id=10 AND 1=1 (verdadeiro) com id=10 AND 1=2 (falso) confirma o controle sobre a lógica.

Um teste complementar elegante é o de concatenação inócua. Em vez de quebrar a query, você a mantém válida com um valor equivalente:

?id=10        → resultado normal
?id=11-1      → se retornar o mesmo de id=10, o banco avaliou a expressão: numérico injetável
?id=10'||''   → em alguns SGBDs, concatena vazio e mantém o resultado: string injetável

Se o alvo avalia a expressão em vez de tratá-la como literal, você confirmou a injeção sem disparar nenhum erro ruidoso — útil quando se quer ser discreto.

Passo 2: explorar UNION-based

Quando o resultado da consulta aparece na página, o ataque UNION é poderoso. Ele exige descobrir o número de colunas:

-- Descobrir a quantidade de colunas (incrementando até dar erro)
?id=10 ORDER BY 1
?id=10 ORDER BY 2
?id=10 ORDER BY 3   -- erro aqui significa 2 colunas

Com o número de colunas conhecido, anexe sua própria consulta:

?id=10 UNION SELECT usuario, senha FROM usuarios

Há duas restrições do UNION que tropeçam quem está começando: o número de colunas do SELECT injetado precisa bater exatamente com o da query original, e os tipos precisam ser compatíveis. Quando você não sabe quais colunas aceitam texto, use NULL como preenchimento e troque uma por vez por uma string conhecida para descobrir qual aparece na página:

?id=10 UNION SELECT NULL, NULL          -- ajusta a contagem
?id=10 UNION SELECT 'aaa', NULL         -- a 1ª coluna é refletida?
?id=10 UNION SELECT NULL, 'bbb'         -- e a 2ª?

A partir daí, você pode extrair tabelas e colunas consultando o catálogo do banco (information_schema), mapeando todo o esquema:

-- Listar tabelas do banco atual
?id=10 UNION SELECT table_name, NULL FROM information_schema.tables
-- Listar colunas de uma tabela de interesse
?id=10 UNION SELECT column_name, NULL FROM information_schema.columns WHERE table_name='usuarios'

Esse é o salto que torna a SQLi devastadora: de um único parâmetro vulnerável, você descobre o esquema inteiro e então extrai qualquer dado.

Passo 3: explorar Blind SQLi

Quando nada volta na resposta, recorra à injeção cega. Na variante booleana, você faz perguntas de sim/não:

-- A primeira letra do nome do banco é 's'?
?id=10 AND substring(database(),1,1)='s'

Se a página retorna "verdadeira", a condição é satisfeita. Caractere a caractere, você extrai dados. Para acelerar, em vez de testar letra por letra, usa-se busca binária sobre o código ASCII de cada caractere — assim cada posição exige cerca de 7 requisições em vez de até 26:

-- O código ASCII do 1º caractere é maior que 109 ('m')?
?id=10 AND ascii(substring(database(),1,1)) > 109

Na variante temporal, induz atrasos:

-- Se a condição for verdadeira, o banco espera 5 segundos
?id=10 AND IF(1=1, SLEEP(5), 0)

Medindo o tempo de resposta, você infere a informação mesmo sem nenhum dado visível. É lento, mas devastadoramente eficaz. Note que a sintaxe de atraso varia por SGBD: SLEEP(5) no MySQL, pg_sleep(5) no PostgreSQL, WAITFOR DELAY '0:0:5' no SQL Server. Identificar o banco-alvo cedo (pelos erros, pelas funções aceitas, pelos cabeçalhos do servidor) define qual dialeto usar.

Automatizando com cuidado

Ferramentas automatizadas (como o sqlmap) detectam e exploram SQLi com rapidez. Em pentest profissional, automação economiza tempo, mas:

  • Entenda primeiro manualmente. Quem só roda ferramenta não sabe interpretar resultados nem evitar falsos positivos.
  • Respeite o escopo. Automação agressiva pode derrubar serviços e gerar carga indevida.
  • Documente cada achado para o relatório.

Um uso responsável do sqlmap parte de uma requisição já capturada e confirmada manualmente, com nível e risco controlados:

# Requisição salva pelo proxy; -p marca o parâmetro suspeito; nível e risco baixos
sqlmap -r requisicao.txt -p id --level=2 --risk=1 --batch

A regra de ouro: a ferramenta confirma e acelera o que você já entendeu; ela não substitui o entendimento. Um pentester que não sabe reproduzir o achado à mão não sabe avaliá-lo nem explicá-lo no relatório.

A SQLi raramente vem sozinha; o mesmo descuido com entrada do usuário leva a falhas como as de XSS na prática: explorando Cross-Site Scripting eticamente.

Impacto real da SQLi

Por que essa falha é tão grave? Uma SQLi bem-sucedida pode:

  • Vazar bancos inteiros: usuários, senhas, dados pessoais e financeiros.
  • Autenticar sem credenciais (authentication bypass).
  • Modificar ou apagar dados com UPDATE e DELETE.
  • Executar comandos no sistema operacional em bancos mal configurados.

Esse potencial coloca a SQLi entre as vulnerabilidades de maior severidade, alimentando a análise descrita em Exploração de vulnerabilidades: do CVE ao exploit. A injeção figura há anos no topo do OWASP Top 10, justamente porque combina alta probabilidade (entrada de usuário é onipresente) com alto impacto (acesso direto aos dados).

A defesa: consultas parametrizadas

Toda essa exploração desaparece quando código e dados são separados. A defesa principal são consultas parametrizadas (prepared statements):

# SEGURO — a entrada nunca vira código SQL
query = "SELECT * FROM usuarios WHERE email = %s AND senha = %s"
cursor.execute(query, (email, senha))

Com parâmetros, o banco trata a entrada como dado, jamais como SQL. Mesmo que o usuário envie ' OR '1'='1, isso vira um valor literal procurado na coluna email — e simplesmente não encontra ninguém. A query é compilada antes de os dados serem ligados, então a estrutura do comando fica imutável.

Halfond, Viegas e Orso apontam a parametrização e a validação de entrada como contramedidas centrais contra a classe inteira de ataques (Halfond, Viegas & Orso, 2006). Complemente com defesa em profundidade:

  • Validação de entrada (tipo, formato, tamanho). Um id que deveria ser inteiro nunca deveria conter aspas.
  • Privilégio mínimo no usuário do banco: a aplicação não precisa de DROP nem de acesso a information_schema.
  • ORMs e query builders ajudam, pois parametrizam por padrão — mas cuidado com APIs de "raw query" que reintroduzem concatenação.
  • Escape como último recurso: parametrizar é sempre melhor; escapar manualmente é frágil e propenso a erro.
  • WAF como camada adicional, nunca como única defesa.

O caso especial de identificadores

Há um ponto que prepared statements não resolvem: nomes de tabela e coluna, e cláusulas como ORDER BY <coluna>, não podem ser parametrizados. Se o usuário escolhe a coluna de ordenação, você não pode passá-la como %s. A defesa aqui é uma allowlist: valide a entrada contra um conjunto fixo de valores permitidos antes de inseri-la na query.

# Identificador NÃO é parametrizável → use allowlist
COLUNAS_PERMITIDAS = {"nome", "data", "preco"}
ordenar_por = entrada if entrada in COLUNAS_PERMITIDAS else "nome"
query = f"SELECT * FROM produtos ORDER BY {ordenar_por}"

Repare que aqui a interpolação é segura porque o valor só pode ser um dos três literais validados — nunca a entrada crua do usuário.

Perguntas frequentes

Um ORM me torna imune a SQLi? Em grande parte, sim, porque ORMs parametrizam por padrão. Mas o risco volta no momento em que você usa "raw queries", interpola valores em fragmentos de SQL, ou monta cláusulas dinâmicas (ORDER BY, nomes de tabela). A vulnerabilidade está no padrão de concatenação, não na ausência de ORM.

WAF não resolve o problema? Um WAF filtra payloads conhecidos e ajuda, mas é contornável (codificações alternativas, comentários inline, variações de sintaxe). Trate-o como alarme e camada extra, nunca como a correção. A correção mora no código: parametrize.

E se a aplicação usa NoSQL? NoSQL não é imune a injeção — existe NoSQL injection (por exemplo, manipulando operadores como $ne ou $gt em queries MongoDB montadas a partir de entrada do usuário). O princípio é o mesmo: nunca confie em entrada para montar a estrutura da consulta.

Validação de entrada sozinha basta? Não. Validação reduz a superfície, mas é frágil como única defesa — sempre existe um caractere legítimo que pode ser abusado. A combinação correta é parametrização (defesa primária) + validação + privilégio mínimo.

Conclusão

Explorar SQL Injection em laboratório revela por que essa falha continua relevante décadas depois: ela quebra a fronteira entre código e dados com entradas triviais. Aprendemos a detectá-la com uma aspa, a confirmar com testes booleanos, a extrair dados com UNION e o information_schema, a contornar respostas silenciosas com injeção cega booleana e temporal e, acima de tudo, a defender com consultas parametrizadas somadas a validação, privilégio mínimo e allowlists para identificadores. O pentester ético explora para entender o impacto e recomendar a correção — nunca para causar dano. Pratique sempre em ambientes autorizados, e leve esse olhar crítico para todo código que você escreve ou revisa, inclusive o que vem de assistentes de IA.

Referências

  • Halfond, Viegas, Orso (2006). A Classification of SQL Injection Attacks and Countermeasures. IEEE ISSSE.
  • Dafydd Stuttard, Marcus Pinto (2011). The Web Application Hacker's Handbook (2nd ed.). Wiley.
  • OWASP (2021). OWASP Top 10: A03 Injection. The OWASP Foundation.

Leituras relacionadas

Nenhum comentário ainda

Seja o primeiro a comentar.

Deixe seu comentário

Entre com sua conta Canverly para comentar. Você pode usar a mesma conta em qualquer site da rede.

Entrar com Canverly