SELECT * FROM users WHERE name=''; DROPTABLE users; --';user input escaped the stringSQL injection

Injeção SQL

11 minutos de leituraSegurança

A injeção de SQL está no topo ou perto do topo de todas as listas de vulnerabilidades de segurança da Web desde o final da década de 1990. A solução — consultas parametrizadas — é conhecida, simples e recomendada há duas décadas. Mesmo assim, novas vulnerabilidades do SQLi ainda são descobertas toda semana. A categoria persiste porque o modo de falha é sutil e os padrões de muitas estruturas são imperfeitos.

O corpo completo do artigo é fornecido em inglês abaixo.

Injeção

SQL (SQLi) é uma classe de vulnerabilidade em que um invasor pode injetar código SQL em uma consulta que o aplicativo envia ao seu banco de dados. Se for bem-sucedido, o invasor poderá ler, modificar ou excluir dados que não está autorizado a acessar, às vezes executar código no servidor de banco de dados e, às vezes, atacar o próprio aplicativo.

O erro clássico

O exemplo clássico. Um aplicativo aceita um nome de usuário de um formulário de login e o procura:

query = "SELECT * FROM users WHERE username='" + input + "';"

Se input for alice, a consulta torna-se:

SELECT * FROM usuários WHERE nomedeusuario='alice';

Normal. Mas se input for alice' OR '1'='1, a consulta se tornará:

SELECT * FROM users WHERE username='alice' OR '1'='1';

The OR '1'='1' é sempre verdadeiro. A consulta retorna todos os usuários da tabela. Com cargas úteis mais sofisticadas, o invasor pode despejar tabelas, modificar registros ou escalar ainda mais.

As categorias de ataque

  • SQLi clássico/em banda. Os resultados da consulta injetada aparecem na resposta do aplicativo. O invasor lê os dados diretamente.
  • Blind SQLi. Nenhum dado aparece na resposta, mas o comportamento do aplicativo muda com base no fato de a consulta injetada retornar verdadeiro ou falso. O invasor extrai os dados um bit de cada vez, fazendo perguntas como "o primeiro caractere da senha do administrador é 'a'?"
  • SQLi. cego baseado em tempo O invasor usa SLEEP() ou equivalente para fazer o banco de dados responder lentamente quando uma condição é verdadeira. Exfiltração de dados ainda mais lenta, mas funciona quando o aplicativo não fornece feedback de resposta.
  • SQLi fora de banda. O invasor engana o banco de dados para fazer uma conexão de rede externa (pesquisa de DNS, solicitação HTTP) que vaza dados para um servidor que eles controlam.
  • SQLi de segunda ordem. A entrada maliciosa é armazenado primeiro no banco de dados e depois lido e usado em outra consulta de forma insegura. A injeção acontece em um contexto diferente da entrada.

A correção: consultas parametrizadas

A maneira correta de consultar um banco de dados com entrada do usuário é usar espaços reservados para parâmetros, nunca concatenar strings. Em essencialmente todas as linguagens:

// Python (psycopg2)
cursor.execute("SELECT * FROM usuários WHERE nomedeusuario=%s", (entrada,))

//Java
stmt = conn.prepareStatement("SELECT * FROM usuários WHERE nomedeusuario=?");
stmt.setString(1, entrada);

// JavaScript (pág.)
client.query("SELECT * FROM users WHERE username=$1", [input]);

O driver envia o SQL e os valores dos parâmetros separadamente. O banco de dados nunca confunde dados de entrada com sintaxe SQL. Mesmo que a entrada contenha aspas, ponto e vírgula, cláusulas OR ou qualquer outra sintaxe SQL, ela será tratada como dados.

Isso funciona. Essa tem sido a abordagem recomendada há duas décadas. O desafio não é a técnica; é garantir que todas as consultas em uma base de código o utilizem.

ORMs não são automaticamente seguros

Mapeadores relacionais de objetos (ActiveRecord, Sequelize, Hibernate, SQLAlchemy) usam consultas parametrizadas por padrão, o que é ótimo. Mas todo ORM tem saídas de escape:

  • raw() métodos que usam strings SQL arbitrárias
  • Cláusulas ORDER BY baseadas em string (nomes de colunas não podem ser parametrizados)
  • Nomes de tabelas interpolados em consultas
  • Procedimentos armazenados que se concatenam

Os bugs modernos do SQLi geralmente aparecem nesses casos extremos, não no caminho principal do código CRUD.

Injeção NoSQL

O padrão não é exclusivo do SQL. MongoDB, Redis, ElasticSearch, GraphQL — qualquer linguagem de consulta que aceite entrada estruturada de usuários pode ser injetável se a entrada não for validada. Os operadores $ne, $gt, $regex do MongoDB são vetores de injeção comuns quando cargas JSON não são estritamente verificadas pelo esquema.

Defesa em profundidade

Além da parametrização consultas:

  • Lcontas de banco de dados com privilégios mínimos. O aplicativo deve se conectar a um usuário que tenha apenas as permissões necessárias. Os caminhos somente leitura usam credenciais somente leitura. Limita os danos quando o SQLi é explorado.
  • Validação de entrada. Restringe as entradas aos formatos esperados. Útil, mas não suficiente por si só.
  • Web Application Firewalls (WAFs). Tentativas óbvias de SQLi de correspondência de padrões. Atacantes contornáveis, mas lentos.
  • Análise estática. Ferramentas que verificam a origem em busca de consultas concatenadas de strings. IDEs modernos sinalizam estes.
  • Proteção de banco de dados. Desative recursos desnecessários (xp_cmdshell em MSSQL, extensões perigosas em PostgreSQL).

Famous SQLi incidentes

  • 2007 TJX — 94 milhões de cartões de crédito. Começou com um compromisso de Wi-Fi, mas SQLi foi usado para exfiltrar bancos de dados internos.
  • 2008 Heartland – 130 milhões de registros de cartão. SQLi deu acesso inicial à rede.
  • 2012 LinkedIn — 117 milhões de hashes de senha exfiltrados via SQLi.
  • 2017 Equifax — Vulnerabilidade do Apache Struts, mas SQLi fazia parte dos dados pós-comprometimento exfiltração.
  • Ao longo de 2020 - inúmeras violações menores em empresas SaaS, sites de comércio eletrônico, agências governamentais.

A categoria tem décadas e ainda está muito viva.

Perguntas frequentes

A injeção de SQL ainda é comum?
Sim. O Top 10 da OWASP lista a injeção (que inclui SQLi) em suas três principais categorias todos os anos. Os programas de recompensa de bugs veem relatórios SQLi todas as semanas. As estruturas modernas reduziram sua frequência nos principais caminhos de código, mas aplicativos legados, casos extremos e saídas de escape ORM mantêm a categoria viva.
Um firewall de aplicativo da Web pode interromper todas as injeções de SQL?
Os WAFs detectam padrões óbvios e impedem invasores pouco sofisticados, mas invasores experientes podem criar desvios — codificações diferentes, inserção de comentários, sintaxe SQL alternativa. WAFs são uma camada útil; eles não substituem a construção segura de consultas no aplicativo.
O HTTPS evita a injeção de SQL?
Não. SQLi é uma vulnerabilidade da camada de aplicação. HTTPS protege os dados em trânsito, mas não valida o conteúdo. A entrada maliciosa do invasor chega via HTTPS, assim como a de todos os outros; o aplicativo é o que o processa ou não com segurança.
Os bancos de dados NoSQL são imunes?
Não. A injeção NoSQL é uma categoria própria. MongoDB, Elasticsearch e outros aceitam entradas de consulta estruturadas que podem ser manipuladas se não forem estritamente validadas. Os padrões de proteção são semelhantes (tratar a entrada do usuário como dados, nunca como estrutura de consulta), mas os ataques específicos são diferentes.
Como faço para testar meu próprio site para injeção de SQL?
Ferramentas como sqlmap automatizam testes para padrões SQLi comuns; OWASP ZAP e Burp Suite incluem scanners. Para os desenvolvedores, a verificação mais confiável é a revisão de código e a análise estática para consultas concatenadas de strings. Os compromissos de teste de penetração incluem especificamente avaliação SQLi.
Explicação da injeção de SQL: a vulnerabilidade da Web que nunca morre