Guia para Iniciantes sobre SQL Injection (E Como Preveni-lo)
Você está construindo uma funcionalidade de busca para sua aplicação. Os usuários digitam o nome de um produto, seu backend consulta o banco de dados e os resultados aparecem. Simples o suficiente—até que alguém digite '; DROP TABLE products;-- naquela caixa de busca.
Isso é SQL injection, e continua sendo uma das vulnerabilidades mais perigosas no desenvolvimento web. O OWASP Top 10 lista “Injection” como um risco crítico de segurança, e SQL injection está no centro dessa categoria. Se você escreve código que interage com um banco de dados—mesmo ocasionalmente—você precisa entender como esse ataque funciona e como detê-lo.
Pontos-Chave
- SQL injection ocorre quando atacantes manipulam consultas ao banco de dados inserindo código malicioso através de entrada de usuário
- Consultas parametrizadas (prepared statements) são a defesa primária, separando código SQL dos dados
- ORMs não são automaticamente seguros quando usam métodos de consulta raw
- Use listas de permissão (allowlists) para identificadores dinâmicos como nomes de tabelas e colunas, que não podem ser parametrizados
- Aplique defesa em profundidade: contas com privilégios mínimos, camadas centralizadas de acesso a dados e testes de segurança em pipelines de CI
O Que É SQL Injection?
SQL injection ocorre quando um atacante manipula as consultas ao banco de dados da sua aplicação inserindo código malicioso através de entrada de usuário. Em vez de tratar a entrada como dados, o banco de dados a executa como comandos.
Considere um formulário de login. Seu backend pode construir uma consulta assim:
SELECT * FROM users WHERE email = 'user@example.com' AND password = 'secret123'
Se você construir essa consulta concatenando strings com entrada de usuário, um atacante pode inserir ' OR '1'='1 como senha. A consulta resultante se torna:
SELECT * FROM users WHERE email = 'user@example.com' AND password = '' OR '1'='1'
Como '1'='1' é sempre verdadeiro, a consulta retorna todos os usuários, contornando completamente a autenticação.
Por Que SQL Injection Ainda Importa
Você pode presumir que frameworks modernos resolveram esse problema. Não resolveram—pelo menos não automaticamente.
Vulnerabilidades de SQL injection aparecem em:
- Endpoints de API aceitando JSON com parâmetros de filtro
- Formulários de busca com múltiplas opções de consulta
- Painéis administrativos com ordenação ou filtragem dinâmica
- Construtores de relatórios com critérios definidos pelo usuário
Qualquer lugar onde a entrada do usuário influencia uma consulta ao banco de dados é uma superfície de ataque potencial. As consequências variam de roubo de dados até destruição completa do banco de dados.
Como a Construção Insegura de Consultas Acontece
A causa raiz é sempre a mesma: tratar entrada de usuário como código SQL confiável em vez de como dados.
Concatenação de strings é a principal culpada:
// Vulnerável - nunca faça isso
const query = `SELECT * FROM products WHERE name = '${userInput}'`;
Template literals e formatação de strings criam o mesmo problema:
# Vulnerável - nunca faça isso
query = f"SELECT * FROM products WHERE category = '{category}'"
Até mesmo ORMs não são automaticamente seguros quando você usa métodos de consulta raw:
// Vulnerável - consultas raw contornam as proteções do ORM
db.query(`SELECT * FROM users WHERE id = ${req.params.id}`);
Prevenção de SQL Injection com Consultas Parametrizadas
A defesa primária contra SQL injection é usar consultas parametrizadas (também chamadas de prepared statements). Estas separam código SQL dos dados, garantindo que entrada de usuário nunca seja executada como comandos.
Aqui está a abordagem segura em JavaScript:
// Seguro - consulta parametrizada
const query = 'SELECT * FROM products WHERE name = ?';
db.query(query, [userInput]);
E em Python:
# Seguro - consulta parametrizada
cursor.execute("SELECT * FROM products WHERE name = %s", (user_input,))
O banco de dados trata o parâmetro como um valor literal, não como código SQL. Mesmo se alguém inserir '; DROP TABLE products;--, o banco de dados busca por um produto com exatamente essa string como seu nome.
O Que Consultas Parametrizadas Não Cobrem
Identificadores (nomes de tabelas, nomes de colunas) não podem ser parametrizados. Se você precisa de ordenação dinâmica:
// Use uma lista de permissão para nomes de colunas
const allowedColumns = ['name', 'price', 'created_at'];
const sortColumn = allowedColumns.includes(userInput) ? userInput : 'name';
const query = `SELECT * FROM products ORDER BY ${sortColumn}`;
Stored procedures são seguros apenas se evitam SQL dinâmico internamente. Uma stored procedure que concatena strings é igualmente vulnerável.
Por Que Escape Não É Suficiente
Escape manual de strings e sanitização são insuficientes por si só. São propensos a erros, específicos do banco de dados e fáceis de implementar incorretamente. Consultas parametrizadas lidam com escape automaticamente e corretamente.
Discover how at OpenReplay.com.
Defesa em Profundidade
Consultas parametrizadas são sua defesa primária, mas camadas adicionais ajudam:
- Contas de banco de dados com privilégios mínimos: O usuário do banco de dados da sua aplicação não deveria ter permissões administrativas como DROP, ALTER ou CREATE USER em produção
- Camada centralizada de acesso a dados: Direcione todas as consultas através de um único módulo que impõe parametrização
- Revisão de código e testes: Inclua verificações de SQL injection no seu processo de revisão e pipeline de CI
Orientações modernas do OWASP ASVS e da iniciativa “Secure by Design” da CISA enfatizam construir segurança no seu processo de desenvolvimento em vez de adicioná-la depois.
Conclusão
SQL injection permanece perigoso porque é fácil de introduzir e devastador quando explorado. A correção é direta: use consultas parametrizadas para todas as operações de banco de dados, valide identificadores com listas de permissão e nunca confie em entrada de usuário.
Torne consultas parametrizadas seu padrão. Seu eu futuro—e seus usuários—agradecerão.
Perguntas Frequentes
Sim. NoSQL injection é uma vulnerabilidade relacionada que afeta bancos de dados como MongoDB. Atacantes podem manipular operadores de consulta através de entrada de usuário. A defesa é similar: use os métodos de consulta integrados do driver do banco de dados em vez de construir consultas a partir de strings, e valide toda entrada de usuário antes de usá-la em consultas.
ORMs fornecem proteção quando você usa seus métodos de consulta padrão. No entanto, a maioria dos ORMs oferece funções de consulta raw que contornam essas proteções. Se você usar métodos SQL raw ou interpolação de strings dentro de consultas ORM, você ainda está vulnerável. Sempre use métodos parametrizados mesmo dentro de código ORM.
Use ferramentas automatizadas como SQLMap ou OWASP ZAP para escanear vulnerabilidades. Inclua testes unitários focados em segurança que tentem payloads de injection. Conduza revisões de código verificando especificamente concatenação de strings em consultas. Considere testes de penetração para aplicações críticas antes da implantação em produção.
Não. Validação de entrada ajuda a reduzir a superfície de ataque, mas nunca deveria ser sua única defesa. Atacantes inteligentes frequentemente conseguem contornar regras de validação. Consultas parametrizadas são a defesa primária porque fundamentalmente separam código de dados. Use validação como uma camada adicional, não como substituto para construção adequada de consultas.
Gain control over your UX
See how users are using your site as if you were sitting next to them, learn and iterate faster with OpenReplay. — the open-source session replay tool for developers. Self-host it in minutes, and have complete control over your customer data. Check our GitHub repo and join the thousands of developers in our community.