Desde que o primeiro banco de dados relacional foi conectado à internet na década de 1990, uma vulnerabilidade tem se mantido de forma ininterrupta, persistente e desafiadora no topo absoluto do ranking canônico OWASP Top 10 (A03:2021 - Injection) e nas listas de riscos críticos mundiais da CWE/SANS: a Injeção de Banco de Dados (SQL Injection - SQLi e NoSQL Injection - NoSQLi).
Apesar de ser uma vulnerabilidade com mais de 25 anos de existência e cuja solução matemática já é perfeitamente conhecida pela ciência da computação, ela continua sendo o vetor de invasão primário responsável por vazamentos de dados catastróficos que expõem centenas de milhões de cartões de crédito, senhas corporativas e cadastros civis da LGPD em todo o mundo. O motivo da sua persistência é simples: a falha humana na escrita de código durante a comunicação entre a linguagem de backend (PHP, Node.js, Python, Java) e o motor do banco de dados.
Por Que a Injeção Acontece? O Erro da Concatenação Bruta
Para entender como um hacker explora um banco de dados, é preciso compreender uma limitação fundamental dos interpretadores SQL (seja MySQL, PostgreSQL, Oracle ou SQL Server): o motor do banco de dados é absolutamente incapaz de distinguir o que é a "instrução lógica de programação legítima programada pelo desenvolvedor" do que é a "string de dados enviada pelo usuário na tela" se ambos forem enviados misturados dentro de uma mesma frase de texto bruto.
Considere o seguinte exemplo de um código PHP clássico (e letalmente vulnerável) para uma tela de login corporativo:
$email = $_POST['email'];
$senha = $_POST['senha'];
$sql = "SELECT * FROM usuarios WHERE email = '" . $email . "' AND senha = '" . $senha . "'";
$resultado = mysql_query($sql);
O programador desenhou essa consulta pressupondo que o usuário digitará no campo de e-mail um endereço comum como joao@empresa.com.br. O atacante, no entanto, digita no campo de e-mail do formulário a seguinte string sintática calculada: ' OR '1'='1' -- .
Quando o servidor PHP concatena essa string do hacker dentro da variável $sql e a envia ao motor MySQL, a instrução executada no servidor transforma-se matematicamente nesta frase:
O que aconteceu no interpretador do banco de dados?
- O caractere de aspas simples (
') digitado pelo hacker fechou prematuramente a string de busca do e-mail original (que ficou vazia''). - O operador lógico
ORfoi introduzido seguido pela verdade tautológica universal ('1'='1'). Como 1 é sempre igual a 1 na matemática, a condição lógicaWHEREdo banco de dados passa a ser avaliada como TRUE (Verdadeira) para absolutamente todas as linhas existentes na tabela de usuários. - A sequência de traços de comentário SQL (
--em MySQL/SQL Server ou#) instrui o motor do banco a ignorar, anular e descartar completamente o restante da consulta original — isto é, a checagem que exigia a verificação da senha foi deletada da execução!
O banco de dados obedece à consulta subvertida e retorna para a aplicação o primeiro registro de usuário da tabela — que em 99% dos portais corporativos corresponde exatamente à conta de Super Administrador / ID 1. O criminoso loga no painel administrativo sem nunca precisar adivinhar a senha da empresa.
As 4 Modalidades Ofensivas do SQL Injection
Nas auditorias de Pentest e caça a ameaças da ByteAbyss, nossos engenheiros classificam o SQL Injection em quatro variantes de exploração de acordo com o retorno exibido pelo servidor:
- In-Band SQLi (UNION-Based): A modalidade de exfiltração em massa mais rápida. O atacante utiliza o operador relacional
UNION SELECTpara acoplar a consulta do formulário da web com buscas na tabela ocultainformation_schema. O hacker consegue despejar e extrair na própria tela do navegador (ou em arquivos JSON de resposta) 100% das tabelas do banco de dados da empresa, incluindo cartões de crédito, senhas com hash bcrypt e segredos da LGPD. - Error-Based SQLi: Ocorre quando o servidor web está configurado com exibição de erros de banco em modo verboso. O atacante força intencionalmente a geração de erros sintáticos (ex: tentando converter uma string em um inteiro) para que a mensagem de exceção devolvida pelo banco revele em seu texto a versão exata do software e nomes de tabelas (ex:
Conversion failed when converting the varchar value 'admin_users' to data type int). - Blind SQL Injection (Injeção Cega - Boolean e Time-Based): A variante mais sofisticada, encontrada em sistemas seguros que não exibem erros na tela nem refletem dados no HTML. Como o hacker não enxerga a resposta na tela, ele descobre o banco de dados fazendo perguntas de "Sim ou Não" via scripts (como a ferramenta sqlmap) que monitoram o tempo de latência do servidor:
' OR IF(SUBSTRING(version(),1,1)='8', SLEEP(5), 0) --. Se o site demorar exatamente 5 segundos para carregar no navegador, o script confirma cegamente que o banco rodando é a versão 8, extraindo todo o banco de dados letra por letra através de milissegundos de atraso na rede! - Out-of-Band (OOB) e Execução Remota de Código (RCE): A consagração mais destrutiva da invasão. Em servidores Microsoft SQL Server rodando como administrador de sistema (
sa), o atacante aciona o procedimento nativo de execução de terminalxp_cmdshellvia injeção:'; EXEC master..xp_cmdshell 'whoami' --. Em bancos MySQL rodando com permissão root no Linux, o hacker usa a instruçãoINTO OUTFILEpara escrever um arquivo de Web Shell em PHP (.php) diretamente na pasta pública do servidor web, tomando o controle computacional total da máquina host por RCE.
NoSQL Injection: O Mito da "Imunidade em Bancos Não-Relacionais"
Com a explosão do desenvolvimento em JavaScript (Node.js) e aplicações modernas na nuvem, muitos programadores adotaram bancos de dados orientados a documentos (NoSQL — como MongoDB, CouchDB, DynamoDB ou Redis) sob o falso mito de que "como não usam a linguagem SQL, são totalmente imunes a injeções". Essa premissa é um perigoso equívoco.
Em uma aplicação Node.js / Express construída com o driver Mongoose sobre o MongoDB, consultas de autenticação costumam ser modeladas recebendo objetos JSON do corpo da requisição:
app.post('/login', async (req, res) => {
const user = await User.findOne({
username: req.body.username,
password: req.body.password
});
});
Se o programador não validar se os campos req.body.username e password são estritamente strings de texto simples, um atacante pode interceptar a requisição HTTP POST no Burp Suite e enviar no corpo em formato JSON não um texto, mas um objeto contendo operadores lógicos nativos de consulta do MongoDB, como o operador de negação $ne (Not Equal / Diferente de) ou $gt (Greater Than / Maior que):
{
"username": "admin",
"password": { "$ne": null }
}
O que acontece dentro do motor do MongoDB? A instrução é compilada como: "Encontre o usuário cujo username seja 'admin' e cuja senha seja qualquer valor diferente de nulo (null)". Como a senha real do administrador no banco de dados obviamente não é nula (ela é um hash válido), a condição lógica de negação é avaliada como TRUE (Verdadeira)! O atacante burla a autenticação do MongoDB instantaneamente e loga como admin sem saber a senha.
A Prevenção Definitiva: Consultas Parametrizadas (Prepared Statements)
A única estratégia técnica 100% infalível, cientificamente comprovada e inegociável para erradicar o SQL Injection de qualquer sistema de software é substituir todas as concatenações de strings pelo uso universal de Consultas Parametrizadas (Parameterized Queries) com Instruções Preparadas (Prepared Statements) no driver do banco de dados (como o PDO em PHP ou JDBC em Java).
$stmt = $pdo->prepare("SELECT * FROM usuarios WHERE email = :email AND senha = :senha");
$stmt->execute([
'email' => $_POST['email'],
'senha' => $_POST['senha']
]);
$usuario = $stmt->fetch();
Por que o Prepared Statement com PDO é imune a injeções? Porque a comunicação de rede entre o servidor PHP e o servidor MySQL acontece em duas etapas de protocolo estritamente separadas:
- Etapa 1 (Compilação da Estrutura Lógica): O PHP envia primeiro para o banco de dados apenas o "esqueleto" lógico da consulta com os marcadores em branco (
:emaile:senha). O motor MySQL compila, analisa e trava a estrutura lógica de execução na memória do servidor. - Etapa 2 (Envio de Dados Binários Isolados): Em um pacote de rede completamente separado, o PHP envia os dados digitados pelo usuário. Como a estrutura lógica da consulta já foi compilada e engessada na etapa 1, o banco de dados trata qualquer coisa enviada na etapa 2 (mesmo que o usuário digite o terrível
' OR '1'='1' ; DROP TABLE usuarios; --) estrita e incondicionalmente como um bloco literal de texto inofensivo que será buscado na coluna de e-mail. É matematicamente impossível ao caractere de aspas subverter a consulta para executar um comando.
Quer Garantir Que Suas Aplicações e Bancos de Dados Estão Imunes?
Nossos auditores de AppSec realizam análises estáticas de código (SAST) e Pentests Gray Box profundos para identificar e sanar qualquer consulta SQL ou NoSQL insegura antes do deploy.
Auditar Segurança de Bancos de Dados com a ByteAbyss