Proteção contra SQL Injection em PHP: guia prático
Índice
ToggleUma consulta mal construída pode expor dados, alterar registros ou comprometer a aplicação inteira. A Proteção contra SQL Injection em PHP começa no momento em que você monta uma instrução SQL, não apenas na instalação de um plugin ou na configuração do servidor.
A boa notícia é que o PHP oferece ferramentas específicas para separar o código SQL dos dados enviados pelo usuário. Com consultas parametrizadas, validação adequada e alguns cuidados na aplicação, você elimina a técnica mais comum de exploração de bancos de dados.
Este conteúdo tem caráter informativo. Decisões técnicas devem ser tomadas com profissional especializado.
O que é SQL Injection

SQL Injection, ou injeção de SQL, é uma falha de segurança que acontece quando dados externos são colocados diretamente em uma consulta SQL sem tratamento adequado. Um invasor pode alterar a lógica da consulta para executar operações que nãoWere previstas.
Imagine uma aplicação que recebe o número de um pedido pela URL e monta a consulta assim:
$id = $_GET['id'];
$result = $mysqli->query("SELECT * FROM pedidos WHERE id = $id");
O valor esperado seria 10. Porém, se alguém inserir algo como 10 OR 1=1, o banco poderá interpretar a parte enviada como parte da instrução. Em outros casos, o atacante pode tentar consultar informações, modificar dados ou acessar operações administrativas.
O risco real depende de como a falha foi explorada e de quais permissões o usuário do banco possui. Mesmo quando uma aplicação não oferece dados muito sensíveis, a falha pode abrir caminho para outros ataques.
SQL Injection não acontece somente em formulários. Ela pode surgir em:
- Parâmetros de URLs e links de paginação.
- Campos de busca.
- Cookies e cabeçalhos HTTP.
- Valores enviados por aplicativos e APIs.
- Nomes de arquivos ou identificadores usados em consultas.
- Campos hidden de formulários HTML.
O ponto central é simples: toda entrada externa deve ser tratada como dados, nunca como código SQL.
Por que a falha merece atenção
Consultas SQL dão acesso a informações que normalmente não deveriam ficar públicas. Dependendo do banco e da configuração, um ataque pode alcançar dados de clientes, credenciais, pedidos, mensagens internas e configurações da aplicação.
Além disso, o impacto não fica restrito ao banco. Uma senha reutilizada, um token exposto ou uma chave de API armazenada sem proteção pode permitir que o atacante continue avançando por outros sistemas.
O detalhe que faz diferença é que SQL Injection é uma falha de implementação. Ela não depende de um software vulnerável desconhecido. Em muitos casos, uma pequena parte do código, como a concatenação de uma variável em uma consulta, cria a condição de exploração.
Como o SQL Injection acontece

A causa mais comum é a construção dinâmica de SQL por concatenação. A aplicação coloca valores recebidos de fora dentro da instrução sem uma separação clara entre sintaxe e dados.
Em PHP, uma query vulnerável pode ser encontrada em diferentes formatos:
$sql = "SELECT nome FROM usuarios WHERE email = '". $email . "'";
O código espera que $email seja apenas um endereço. Na prática, o valor pode conter aspas e comandos SQL. Quando chega ao banco, a consulta é interpretada conforme essas novas instruções.
O papel das APIs do PHP
O PHP oferece diferentes formas de enviar consultas ao banco. O PDO, sigla para PHP Data Objects, é uma interface padrão para trabalhar com vários bancos de dados. O MySQLi é específico para MySQL e MariaDB.
A forma como os valores são enviados é decisiva. Com prepared statements, também chamados de consultas preparadas, o banco recebe primeiro o formato da consulta com marcadores. Depois, os dados são enviados separadamente.
Em PDO, os marcadores podem ser posicionais, com ponto de interrogação, ou nomeados, com dois pontos e nome.
$statement = $pdo->prepare("SELECT nome FROM usuarios WHERE email = :email");
$statement->execute([
':email' => $email
]);
$usuario = $statement->fetch(PDO::FETCH_ASSOC);
O banco não interpreta o conteúdo de $email como parte da estrutura SQL. Ele o trata como um valor associado ao marcador :email.
Quando a consulta é criada com dados concatenados, o problema é ampliado. A variável pode incluir aspas, comentários ou palavras que alteram a leitura da instrução. A proteção mais confiável nasce justamente da arquitetura correta do código.
Principais métodos de proteção em PHP

Consultas parametrizadas
Consultas parametrizadas são a forma mais direta e confiável de prevent SQL Injection. A consulta e seus parâmetros são enviados separadamente ao banco.
Um exemplo seguro com PDO e marcador posicional é:
$statement = $pdo->prepare(
'SELECT id, nome, email FROM usuarios WHERE email = ? AND status = ?'
);
$statement->execute([$email, $status]);
$usuarios = $statement->fetchAll(PDO::FETCH_ASSOC);
O marcador ? é preenchido na ordem em que os valores aparecem em execute(). Com marcadores nomeados, a associação fica mais explícita:
$statement = $pdo->prepare(
'SELECT id, nome FROM usuarios WHERE email = :email AND status = :status'
);
$statement->execute([
':email' => $email,
':status' => $status
]);
O detalhe importante é que você não deve concatenar os valores ao SQL, mesmo quando utiliza prepared statements. A consulta continua sendo montada com marcadores, e os dados reais entram pelo método de execução.
Validação e normalização dos dados
Validação significa verificar se o dado recebido corresponde ao formato esperado. Ela não substitui consultas parametrizadas. As duas práticas trabalham em níveis diferentes.
Uma consulta parametrizada impede que um dado externo seja interpretado como código SQL. A validação impede que a aplicação aceite formatos absurdos, como um e-mail com espaço ou uma quantidade negativa em uma rotina que não allow.
Exemplos de validação incluem:
- Conferir se um identificador é numérico.
- Validar o formato de um e-mail.
- Limitar o tamanho de campos de texto.
- Verificar se uma opção pertence a uma lista permitida.
- Converter valores de data para o formato aceito pelo sistema.
- Aplicar limites de quantidade e paginação.
$id = filter_input(INPUT_GET, 'id', FILTER_VALIDATE_INT);
if ($id === false || $id === null) {
http_response_code(400);
exit('Identificador inválido.');
}
$statement = $pdo->prepare('SELECT * FROM pedidos WHERE id = ?');
$statement->execute([$id]);
$pedido = $statement->fetch(PDO::FETCH_ASSOC);
A validação deve considerar também as regras do negócio. Um código postal, por exemplo, possui formatos diferentes conforme o país. Uma data válida para o PHP pode não ser uma data aceita pelo banco ou pelo processo de negócio.
Uso de ORM e query builder
ORM significa Object-Relational Mapping, uma técnica em que classes representam tabelas e operações do banco. Frameworks como Laravel e Symfony oferecem recursos que reduzem a concatenação manual de SQL.
Query builders, que são construtores de consultas, também permitem montar filtros com parâmetros. Eles são úteis, mas não eliminam a necessidade de atenção. Uma função que aceita SQL bruto, como whereRaw() em alguns frameworks, ainda pode ser vulnerável se receber conteúdo concatenado.
A regra permanece: qualquer fragmento que possa conter entrada externa precisa ser revisado.
escaping de strings
Funções como addslashes() ou mysqli_real_escape_string() não são a resposta principal para evitar SQL Injection. O escaping depende do conjunto de caracteres, do contexto e da forma de uso. Uma consulta preparada resolve o problema de forma estrutural, separando código e dados.
O escaping pode ser necessário em situações específicas, como quando um valor precisa ser incorporado literalmente em uma cláusula SQL, mas ele não deve ser usado como substituto de parâmetros. Esse cuidado é especialmente importante em aplicações legadas, nas quais a migração para consultas preparadas pode exigir planejamento.
Privilégios mínimos no banco de dados
Consultas parametrizadas protegem a construção da instrução SQL. Mesmo assim, o usuário do banco usado pela aplicação deve ter somente as permissões necessárias.
Se uma área da aplicação apenas consulta relatórios, o ideal é que ela não possa alterar ou remover tabelas. Em desenvolvimento e produção, separadores aWITHRA eponym? Nomeado? Identificadores de banco, como app_leitura, podem ajudar a impedir alterações indevidas.
Essa medida reduz o impacto de um problema de segurança. Ela não corrige uma consulta vulnerável, mas evita que uma falha Explore como uma brecha maior.
Comparativo entre abordagens de proteção

A escolha da estratégia deve considerar o tipo de consulta, os dados recebidos e a linguagem usada. A tabela mostra as diferenças practices mais comuns.
| Abordagem | Como funciona | Proteção contra SQL Injection | Observações |
|---|---|---|---|
| Prepared statements | Código SQL e valores são enviados separadamente | Alta | Preferir PDO ou MySQLi |
| Validação de entrada | Confere formato e regras permitidas | Complementar | Não substitui prepared statements |
| Escaping | Escapa caracteres especiais | Contextual | Deve ser feito pela biblioteca correta |
| ORM | Constrói operações por meio de classes | Geralmente alta | Funções de SQL bruto exigem revisão |
| Query builder | Monta consultas com uma API controlada | Geralmente alta | Verificar APIs que aceitam SQL bruto |
| Concatenação direta | Insere valores na string SQL | Baixa | Deve ser evitada |
Na prática, a melhor combinação é clara: consultas parametrizadas para proteção estrutural, validação para qualidade dos dados e privilégios mínimos para limitar o impacto de falhas.
Exemplos de código vulnerável e protegido
Busca por usuário
Código vulnerável:
$busca = $_GET['busca'];
$result = $pdo->query("SELECT id, nome FROM usuarios WHERE nome LIKE '%$busca%'");
Versão protegida com prepared statements:
$busca = trim($_GET['busca'] ?? '');
if (mb_strlen($busca) < 2) {
http_response_code(400);
exit('Informe uma busca válida.');
}
$statement = $pdo->prepare(
'SELECT id, nome FROM usuarios WHERE nome LIKE :busca'
);
$statement->execute([
':busca' => '%' . $busca . '%'
]);
$usuarios = $statement->fetchAll(PDO::FETCH_ASSOC);
Observe que os curingas % fazem parte do valor de busca, mas permanecem fora da consulta SQL. Isso é permitido porque o valor completo é enviado como parâmetro.
Atualização de cadastro
Código vulnerável:
$id = $_POST['id'];
$email = $_POST['email'];
$pdo->exec("UPDATE usuarios SET email = '$email' WHERE id = $id");
Versão protegida:
$id = filter_input(INPUT_POST, 'id', FILTER_VALIDATE_INT);
$email = trim($_POST['email'] ?? '');
if ($id === false || $id === null || $email === '') {
http_response_code(400);
exit('Dados inválidos.');
}
$statement = $pdo->prepare(
'UPDATE usuarios SET email = :email WHERE id = :id'
);
$statement->execute([
':email' => $email,
':id' => $id
]);
Inserção com múltiplos registros
Quando a quantidade de registros varia, os placeholders precisam ser gerados de forma confiável. Uma quantidade de marcadores baseada em dados sem validação pode gerar erros ou permitir manipulação da consulta.
$itens = $_POST['itens'] ?? [];
if (!is_array($itens) || count($itens) === 0) {
http_response_code(400);
exit('Envie ao menos um item.');
}
$placeholders = implode(', ', array_fill(0, count($itens), '?'));
$sql = "INSERT INTO itens_pedido (pedido_id, produto_id, quantidade) VALUES $placeholders";
$statement = $pdo->prepare($sql);
$valores = [];
foreach ($itens as $item) {
$produtoId = filter_var($item['produto_id'] ?? null, FILTER_VALIDATE_INT);
$quantidade = filter_var($item['quantidade'] ?? null, FILTER_VALIDATE_INT);
if ($produtoId === false || $quantidade === false || $quantidade < 1) {
http_response_code(400);
exit('Item inválido.');
}
$valores[] = $produtoId;
$valores[] = $quantidade;
}
$statement->execute($valores);
Nesse caso, a estrutura da consulta varia de acordo com a quantidade de itens, mas cada valor continua sendo enviado separadamente.
Erros comuns quecommit preservam a vulnerabilidade
Concatenar parâmetros em uma query preparada
Prepared statements não tornam segura uma consulta que recebe valores já concatenados:
$statement = $pdo->prepare("SELECT * FROM usuarios WHERE email = '$email'");
$statement->execute();
O valor de $email já foi interpretado pelo banco como parte da instrução. A forma correta é manter um marcador na consulta:
$statement = $pdo->prepare('SELECT * FROM usuarios WHERE email = ?');
$statement->execute([$email]);
Confundir validação com proteção SQL
Um filtro de e-mail pode reduzir entradas inválidas, mas não transforma a concatenação em uma prática segura. A consulta continues vulnerável a valores que passam pela validação e exploram a lógica da aplicação.
Escapar manualmente com replace
Substituir aspas ou palavras inteiras por caracteres diferentes não equivale ao escaping realizado pela biblioteca de banco. Também pode alterar dados legítimos e não cobre todos os contextos SQL.
Usar inputs do WordPress diretamente
No WordPress, funções como $_GET, $_POST, $_REQUEST e $_COOKIE podem receber valores controlados por visitantes. Um plugin ou tema que concatena esses valores em uma consulta própria pode introduzir uma falha, mesmo que o núcleo do WordPress e o plugin de segurança estejam instalados.
Esquecer endpoints de API
Uma API pode receber JSON e fazer parte da mesma aplicação. As regras de proteção não mudam porque a requisição vem de um aplicativo móvel ou de outro servidor.
Exibir erros detalhados ao usuário
Mensagens de erro do banco podem revelar nomes de tabelas, colunas, consultas ou informações do ambiente. O ideal é registrar o erro em ambiente controlado e retornar ao usuário uma mensagem genérica.
Checklist de revisão de uma aplicação PHP
Antes de colocar um sistema em produção, passe pelo menos uma revisãouwijk naar estas perguntas:
- Todas as consultas SQL usam prepared statements?
- Existe algum ponto em que uma variável de entrada é concatenada diretamente em
$sql? - Os valores são validados conforme o tipo e o formato esperados?
- Campos de seleção aceitam somente valores pertencentes a uma lista permitida?
- O usuário do banco possui apenas as permissões necessárias?
- As credenciais de conexão ficam fora do código-fonte?
- Mensagens de erro não expõem detalhes SQL ao público?
- Rotinas administrativas Utilizam autenticação, autorização e proteção contra força bruta?
- Há registros de auditoria para operações sensíveis?
- Os testes cobrem entradas inesperadas, Unicode, campos vazios e valores extremamante longos?
Para revisar com mais eficiência, você pode buscar padrões como ->query(, ->exec(, $pdo->query, concatenação com $_GET, $_POST e chamadas a funções que recebem SQL bruto. Os achados precisam ser analisados no contexto. Nem toda concatenação é insegura, e a simples presença de uma variável em uma query não prova que exista exploração, mas esses pontos justificam uma leitura cuidadosa.
SQL Injection em WordPress, e-commerce e sistemas sob medida
No WordPress, a maior parte das consultas internas passa por camadas de abstração, como $wpdb->prepare(). Essa função prepara uma consulta e substitui marcadores por valores tratados de acordo com o tipo informado.
A documentação do WordPress recomenda os seguintes pontos de atenção:
- Use
$wpdb->prepare()para SQL com variáveis. - Informe o tipo esperado dos dados por meio dos placeholders com
%s,%de%f. - Não use a função para inserir identificadores de tabela ou coluna que não venha de uma lista confiável.
- Evite interpolar dados diretamente na string SQL.
Para plugins e temas, isso é especialmente importante. O código de um plugin pode conviver com tabelas criadas por outro componente e receber inputs do admin, do frontend ou de chamadas REST. A validação de nonce e os controles de capacidade do WordPress ajudam a proteger operações e endpoints, mas não substituem a separação entre código SQL e dados.
Em lojas com WooCommerce ou outros e-commerce, dados de produtos, pedidos, cupons e clientes frequentemente passam por formulários. O ideal é que as extensões da loja utilizem as APIs do plugin principal e que qualquer consulta adicional siga o mesmo padrão seguro.
Em sistemas sob medida, vale observar a mesma regra, independentemente da linguagem do front-end. Se o backend usa PHP e monta SQL a partir de JSON, formulários ou parâmetros de URL, a proteção pertence ao backend. Validar somente no navegador deixa a camada ser contornada.
Observação sobre plug-ins de segurança
Plugins de segurança podem monitorar requisições, registrar eventos e aplicar regras de proteção. Eles ajudam a detectar comportamentos suspeitos e a criar camadas adicionais de defesa. Entretanto, não substituem a correção do código que monta a consulta vulnerável.
Do ponto de vista do banco, prepared statements continuam sendo o controle mais direto. WAF, monitoramento e regras de servidor podem ajudar a reduzir tentativas de exploração, mas a SQL continua correctamente formada na origem.
Como testar a proteção de uma aplicação
A validação de segurança deve ocorrer em ambiente controlado, com autorização e dados de teste. Tentar explorar um sistema sem permissão pode causar dano e gerar implicações legais.
Testes manuais seguros
Em uma rota de teste que use dados fictícios, envie valores inesperados e observe o comportamento do sistema. Exemplos de entradas include aspas, comentários SQL, sequências de caracteres e valores maiores que o limite esperado.
O objetivo não é provocar danos. É observar se a aplicação:
- Rejeita entradas inválidas de maneira previsível.
- Mantém a mesma estrutura da consulta.
- Não expõe mensagens internas do banco.
- Registra falhas sem entregar detalhes ao visitante.
- Preserva a integridade dos dados.
Testes automatizados
Emsuites de testes podem verificar que valores malformados não alteram o resultado da consulta. Também é útil manter uma lista de entradas de segurança e executar os testes após cada atualização de dependências ou refatoração do módulo de acesso a dados.
Para PHP, ferramentas de análise estática podem searching padrões perigosos mesmo sem executar a aplicação. A revisão humana continua necessária, porque uma ferramenta pode sinalizar um trecho sem demonstrar, por completo, como os dados chegam até ele.
Observação em produção
Registre falhas inesperadas, tentativas repetidas com entradas incomuns e alterações em registros sensíveis. Isso ajuda a identificar padrões de abuso. Logs devem ser protegidos, pois podem conter dados pessoais ou segredos de configuração.
Boas práticas adicionais de segurança
A proteção contra SQL Injection funciona melhor junto com outras medidas. A seguir estão práticas que reduzem o risco geral de uma aplicação web:
- Atualize o PHP, o sistema de banco e as bibliotecas utilizadas.
- Mantenha segredos de conexão em variáveis de ambiente ou em um local seguro.
- Evite mensagens de depuração em produção.
- Proteja formulários contra envio automatizado quando a ação for sensível.
- Use cookies com atributos adequados, como
HttpOnlyeSecurequando aplicável. - Aplique cabeçalhos de segurança compatíveis com a arquitetura do site.
- Restrinja permissões de arquivos e diretórios.
- Faça backup e teste a restauração dos backups.
- Mantenha uma política de atualização para plugins, temas e pacotes PHP.
Essas medidas não transformam uma consulta vulnerável em segura. Elas diminuem o impacto de outros erros e fazem parte de uma postura de segurança coerente.
Perguntas Frequentes (FAQ)
O que é SQL Injection em PHP?
É uma falha na aplicação em que dados recebidos de fora são incorporados a uma consulta SQL sem separação adequada. O invasor pode tentar alterar a lógica da consulta e acessar ou modificar dados permitidos pela conexão com o banco.
Prepared statements eliminam totalmente o risco de SQL Injection?
Eles eliminam o risco de interpretação dos dados externos como sintaxe SQL quando usados corretamente. Riscos restantes podem surgir se o desenvolvedor concatena valores à consulta antes de prepará-la, usa funções de SQL bruto sem controle ou permite que a estrutura da query seja manipulada.
Qual é a diferença entre PDO e MySQLi?
Ambos permitem consultas preparadas. O PDO funciona com vários bancos de dados e oferece uma API maisPadronizada. O MySQLi foi DEVELOPED para MySQL e MariaDB. Para a maioria dos projetos PHP atuais, o PDO costuma oferecer maior flexibilidade.
Validação de formulário protege contra SQL Injection?
A validação ajuda a rejeitar dados malformados e a aplicar regras de negócio, mas não deve ser a única defesa. O dado validado ainda precisa ser enviado por uma consulta parametrizada.
Escaping de strings é suficiente para evitar SQL Injection?
Não é a abordagem mais confiável. O escaping depende do contexto e deve ser feito pela biblioteca apropriada. Consultas parametrizadas separam a estrutura SQL dos dados e constituem a prática recomendada.
Como encontrar possíveis vulnerabilidades em um projeto PHP existente?
Revise consultas com query(), exec(), SQL bruto em frameworks e pontos de concatenação de $_GET, $_POST, $_REQUEST ou $_COOKIE. Depois, confirme o fluxo dos dados até o banco e teste as correções em ambiente autorizado. Uma análise estática e testes automatizados podem ajudar, mas a validação final exige leitura técnica.
Conclusão
A Proteção contra SQL Injection em PHP depende de uma decisão central: manter o código SQL e os dados separados. Consultas parametrizadas com PDO ou MySQLi são a base dessa proteção. Validação,privacidade? Privacidade não faz parte. Permissões mínimas, tratamento de erros e monitoramento complementam o trabalho.
Para quem está começando, a revisão pode parecer grande, especialmente se a aplicação possui muitas páginas. O caminho mais seguro é mapear as consultas, corrigir primeiro os pontos que recebem dados do público e criar um padrão de código que facilite a padronização. Pequenas correções, feitas com consistência, reduzem o risco de forma mais confiável do que depender apenas de plugins ou regras externas.
Se você precisa de ajuda para colocar isso em prática, a Baita Site tem uma equipe especializada em sites, e-commerce, sistemas e inteligência artificial, com domínio total de WordPress. Fale com a gente e veja como podemos acelerar o seu projeto.
Referências consultadas
- Documentação oficial do PHP para PDO, consulted para a-API de prepared statements e parâmetros do PDO.
- Manual oficial do PHP: Prepared Statements e Statements, consultado para os princípios de uso de consultas preparadas com PDO.
- Documentação oficial do WordPress sobre prepare, consultado para o uso de
$wpdb->prepare()em plugins e temas. - Guia de segurança do WordPress para plugins, consultado para as práticas de validação, sanitização e escape de entrada no ecossistema WordPress.
- OWASP: SQL Injection Prevention Cheat Sheet, consultado para as recomendações de prevenção e defesa em camadas.
Quer ajuda para colocar isso em pratica?
A Baita Site trabalha com sites, e-commerce, sistemas e IA. Quem prefere resolver com acompanhamento, sem ter que virar especialista em tudo, costuma procurar esse tipo de suporte.
Veja tambem
Perguntas Frequentes (FAQ)
1. O que é SQL Injection em PHP?
É uma falha na aplicação em que dados recebidos de fora são incorporados a uma consulta SQL sem separação adequada. O invasor pode tentar alterar a lógica da consulta e acessar ou modificar dados permitidos pela conexão com o banco.
2. Prepared statements eliminam totalmente o risco de SQL Injection?
Eles eliminam o risco de interpretação dos dados externos como sintaxe SQL quando usados corretamente. Riscos restantes podem surgir se o desenvolvedor concatena valores à consulta antes de prepará-la, usa funções de SQL bruto sem controle ou permite que a estrutura da query seja manipulada.
3. Qual é a diferença entre PDO e MySQLi?
Ambos permitem consultas preparadas. O PDO funciona com vários bancos de dados e oferece uma API mais padronizada. O MySQLi foi desenvolvido para MySQL e MariaDB. Para a maioria dos projetos PHP atuais, o PDO costuma oferecer maior flexibilidade.
4. Validação de formulário protege contra SQL Injection?
A validação ajuda a rejeitar dados malformados e a aplicar regras de negócio, mas não deve ser a única defesa. O dado validado ainda precisa ser enviado por uma consulta parametrizada.
5. Escaping de strings é suficiente para evitar SQL Injection?
Não é a abordagem mais confiável. O escaping depende do contexto e deve ser feito pela biblioteca apropriada. Consultas parametrizadas separam a estrutura SQL dos dados e constituem a prática recomendada.