Situação atual: PHP2Shell. Duas vulnerabilidades críticas do PHP (CVSS 9,2) permitem executar código malicioso sem autenticação. Patches disponíveis desde 21/10/2026.O que fazer agora →

Evitar SQL Injection em PHP

Como os programadores excluem de forma fiável a SQL Injection em aplicações PHP com Prepared Statements, listas de permissão e permissões mínimas na base de dados.

Atualizado em 6 min de leituraGuias

Em resumo: evita a SQL Injection em PHP de forma fiável nunca inserindo valores em textos SQL, mas passando-os sempre através de Prepared Statements com marcadores de posição. Para nomes de colunas e de tabelas, que não podem ser passados como marcadores, aplica-se uma lista de permissão fixa. Adicionalmente, um utilizador de base de dados com permissões mínimas limita o dano, caso ainda reste uma vulnerabilidade.

Porque é que a SQL Injection não pode sair do radar após o PHP2Shell

Em outubro de 2026, muita coisa gira à volta do PHP2Shell, duas vulnerabilidades críticas no interpretador PHP, corrigidas desde 21/10/2026 no PHP 8.5.12, 8.4.27, 8.3.36 e 8.2.35. Muitas equipas atualizaram servidores e reiniciaram processos nesses dias. Isso está certo e é urgente.

Mas um estado atual do PHP nada diz sobre o código próprio. A SQL Injection é há anos uma das fragilidades mais frequentes em aplicações web e consta do OWASP Top 10 na categoria Injection. Quem de qualquer modo planeia verificações de segurança deve olhar logo também para os acessos à base de dados da própria aplicação.

A boa notícia: a SQL Injection é uma das poucas fragilidades que se podem excluir quase por completo com uma regra simples, aplicada de forma consequente.

Como surge a SQL Injection em PHP?

A SQL Injection surge quando uma aplicação trata as entradas do utilizador como parte de um comando SQL, em vez de dados puros. Típico é compor uma consulta a partir de blocos de texto e variáveis. Se um visitante puder determinar o conteúdo de uma dessas variáveis, pode, em certas circunstâncias, alterar o significado do comando.

As consequências vão da leitura de dados de clientes alheios à alteração de preços ou permissões e à eliminação de tabelas inteiras. Consoante a base de dados e as permissões, uma vulnerabilidade pode também permitir acessos para além da base de dados. Se estiverem afetados dados pessoais, pode surgir uma obrigação de notificação nos termos do RGPD.

As entradas não são apenas campos de formulário. Também parâmetros de URL, cookies, cabeçalhos HTTP, ficheiros carregados, respostas de interfaces alheias e dados já guardados na própria base de dados são considerados não fiáveis.

Prepared Statements com PDO e mysqli

Os Prepared Statements enviam o comando SQL e os valores em separado para a base de dados. Os valores nunca são interpretados como parte do comando. Com PDO, uma consulta segura é assim:

<?php
$pdo = new PDO($dsn, $benutzer, $passwort, [
    PDO::ATTR_ERRMODE            => PDO::ERRMODE_EXCEPTION,
    PDO::ATTR_EMULATE_PREPARES   => false,
    PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
]);

$stmt = $pdo->prepare('SELECT id, name, email FROM kunden WHERE id = :id AND status = :status');
$stmt->execute([
    'id'     => (int) $_GET['id'],
    'status' => 'aktiv',
]);
$kunde = $stmt->fetch();

A opção PDO::ATTR_EMULATE_PREPARES => false faz com que a base de dados use verdadeiros Prepared Statements, desde que o controlador o suporte. Com mysqli aplica-se o mesmo princípio:

<?php
$stmt = $mysqli->prepare('SELECT id, name FROM produkte WHERE kategorie = ? AND preis <= ?');
$stmt->bind_param('sd', $kategorie, $maxPreis);
$stmt->execute();
$ergebnis = $stmt->get_result();

Também as listas para IN (...) podem ser representadas com segurança, criando um marcador próprio para cada valor e passando os valores em separado.

<?php
$ids = array_map('intval', $ausgewaehlteIds);
$platzhalter = implode(',', array_fill(0, count($ids), '?'));
$stmt = $pdo->prepare("SELECT id, name FROM produkte WHERE id IN ($platzhalter)");
$stmt->execute($ids);

Casos especiais: nomes de colunas, ordenação e SQL em bruto

Os marcadores funcionam apenas para valores, não para nomes de tabelas, nomes de colunas ou sentidos de ordenação. É precisamente aqui que, na prática, surgem muitas vulnerabilidades, por exemplo em tabelas ordenáveis ou filtros. A solução é uma lista fixa de valores permitidos:

<?php
$erlaubteSpalten = ['name' => 'name', 'preis' => 'preis', 'datum' => 'erstellt_am'];
$spalte   = $erlaubteSpalten[$_GET['sort'] ?? ''] ?? 'name';
$richtung = (($_GET['dir'] ?? '') === 'desc') ? 'DESC' : 'ASC';

$stmt = $pdo->query("SELECT id, name, preis FROM produkte ORDER BY $spalte $richtung");

As frameworks e os CMS trazem ferramentas próprias. No WordPress, o $wpdb->prepare() assume esta tarefa, no Laravel e no Symfony os query builders e ORMs. Assim que usar aí SQL em bruto, por exemplo através de funções com “raw” no nome, voltam a aplicar-se as regras desta secção. Indicações sobre o Laravel encontra-as em Proteger aplicações Laravel.

Dica

Procure no código, de forma orientada, textos SQL em que se inserem variáveis. Um começo simples é uma pesquisa por query( e ->raw em combinação com $_GET, $_POST ou $_REQUEST.

Detetar SQL Injection no código existente

Em software novo, a regra pode ser cumprida desde o início. É mais difícil em aplicações que cresceram ao longo de anos. Aí vale a pena uma pesquisa sistemática dos pontos em que textos SQL são compostos com variáveis:

# Comandos SQL em que se inserem variáveis
grep -rnE "(SELECT|INSERT|UPDATE|DELETE)[^;]*\\\$[a-zA-Z_]" src/ --include="*.php" | head -50
# Utilização direta de dados do pedido em funções de consulta
grep -rnE "(query|exec|raw)\s*\(.*\\\$_(GET|POST|REQUEST|COOKIE)" src/ --include="*.php"

Nem todo o resultado é uma vulnerabilidade, por exemplo quando a variável contém apenas um nome de tabela fixo. Cada ocorrência deve, porém, ser verificada conscientemente uma vez e, se necessário, convertida para Prepared Statements. As ferramentas de análise estática de código ajudam a detetar estes padrões de forma permanente no pipeline de build, de modo que novos erros nem cheguem à produção.

Limitar o dano: permissões, mensagens de erro, verificação

Código seguro é a primeira linha de defesa. Outros níveis limitam o dano, caso uma vulnerabilidade passe despercebida:

  • Permissões mínimas: o utilizador de base de dados do site precisa geralmente apenas de SELECT, INSERT, UPDATE e DELETE na base de dados própria, sem permissões para criar utilizadores ou aceder a outras bases de dados.
  • Sem mensagens de erro para o exterior: display_errors = Off no php.ini. Os erros da base de dados pertencem ao registo, não ao navegador.
  • Separação por aplicação: cada site recebe um utilizador de base de dados próprio.
  • Manter as dependências atualizadas: há vulnerabilidades também em bibliotecas, como mostra o guia composer audit.
  • Mandar verificar: a revisão de código e a análise automatizada encontram muitos pontos; um teste de intrusão mostra o que é realmente acessível a partir do exterior.
-- Exemplo: utilizador de base de dados apenas com as permissões necessárias
CREATE USER 'shop_app'@'localhost' IDENTIFIED BY '<starkes Passwort>';
GRANT SELECT, INSERT, UPDATE, DELETE ON shop.* TO 'shop_app'@'localhost';

Uma fragilidade aparentada diz respeito à saída de dados no navegador. Como a proteger, explica-o o guia Evitar Cross-Site Scripting em PHP.

Como ajuda concretamente a PHP SOS?

  • Verificamos de forma sistemática os acessos à base de dados da sua aplicação quanto a consultas inseguras.
  • Priorizamos os achados segundo a acessibilidade e os dados afetados e mostramos correções concretas.
  • Levamos as permissões da base de dados e o tratamento de erros a um nível seguro.
  • Voltamos a testar após a correção e documentamos o resultado.

Uma verificação independente é descrita no serviço Auditoria de segurança e teste de intrusão.

Perguntas frequentes

Basta tratar as entradas com addslashes ou real_escape_string?

Não, é propenso a erros. O escaping depende do conjunto de caracteres e do contexto e é facilmente esquecido num ponto. Os Prepared Statements separam, por princípio, o comando e os dados e são, por isso, o caminho recomendado.

Um ORM protege automaticamente contra SQL Injection?

Em grande medida, desde que utilize as funções de consulta do ORM. Assim que escreve SQL em bruto ou insere valores em ordenações e nomes de colunas, aplicam-se as mesmas regras que sem ORM.

Uma Web Application Firewall ajuda contra a SQL Injection?

Uma WAF pode intercetar padrões conhecidos e ganhar tempo. Mas não substitui código seguro, porque as regras baseadas em dados do pedido podem ser contornadas e por vezes atingem entradas legítimas.

A atualização do PHP contra o PHP2Shell também fecha vulnerabilidades de SQL Injection?

Não. O PHP2Shell afeta o interpretador PHP. A SQL Injection é um erro no código da aplicação e mantém-se, seja qual for a versão do PHP em execução.

Fontes

Contacto imediato

Cada hora conta. Escreva-nos agora.

Descreva brevemente o sistema, a versão do PHP e o que observou. Entramos em contacto consigo, esclarecemos a situação e trabalhamos remotamente até o incidente estar concluído e documentado.

Ajuda imediata