Consultas Preparadas PDO: 100% de proteção contra injeção SQL?

Treinador Online de Python para Iniciantes

Aprenda Python facilmente sem sobrecarga teórica. Resolva tarefas práticas com verificação automática e escreva código diretamente no navegador.

Start Course

Introdução: a pergunta que atormenta todo desenvolvedor PHP

\n\n

Se você já escreveu consultas SQL em PHP, certamente já ouviu o mantra: "Use PDO e prepared statements — e estará seguro". Mas será que é realmente assim? No Stack Overflow, a pergunta "Are PDO prepared statements sufficient to prevent SQL injection?" acumulou milhares de visualizações e dezenas de discussões acaloradas. E não é à toa.

\n\n

Neste artigo, não vamos dar apenas uma resposta curta de "sim" ou "não". Vamos mergulhar sob o capô do PDO, entender exatamente como funcionam as consultas preparadas e descobrir em quais casos até o PDO pode te decepcionar. Pronto? Vamos lá.

\n\n

O que é injeção SQL e por que ela é perigosa?

\n\n

Antes de falar sobre proteção, vamos relembrar o inimigo de vista. Injeção SQL é um ataque onde um invasor "injeta" código SQL malicioso através de dados de entrada. O clássico:

\n\n
// Código ruim: nunca faça isso\n$username = $_POST['username'];\n$query = "SELECT * FROM users WHERE username = '$username'";\n$result = mysqli_query($conn, $query);\n
\n\n

Se o invasor enviar admin' OR '1'='1, a consulta se torna:

\n\n
SELECT * FROM users WHERE username = 'admin' OR '1'='1'\n
\n\n

Pronto — acesso aos dados sem senha. E se enviar '; DROP TABLE users; --? Adeus, tabela de usuários.

\n\n

PDO e consultas preparadas: como funciona?

\n\n

PDO (PHP Data Objects) oferece um mecanismo de prepared statements (expressões preparadas). A ideia é simples: você separa a estrutura da consulta dos dados. Em vez de inserir variáveis diretamente na string, você usa placeholders:

\n\n
// Código seguro com PDO\n$stmt = $pdo->prepare("SELECT * FROM users WHERE username = :username");\n$stmt->execute([':username' => $_POST['username']]);\n
\n\n

Mas o que acontece sob o capô? Este é o ponto crucial que muitos ignoram.

\n\n

Modos de emulação vs. prepared statements nativos

\n\n

O PDO pode operar em dois modos:

\n\n
    \n
  • Modo de emulação (padrão em versões antigas do PHP) — o próprio PDO faz o escape dos dados e os insere na consulta do lado do PHP. Ainda é seguro, mas teoricamente vulnerável se o escape falhar (por exemplo, ao trabalhar com codificações não padrão).
  • \n
  • Modo nativo — o PDO envia ao servidor do banco de dados duas mensagens: primeiro o template da consulta, depois os dados separadamente. O servidor processa os dados como valores, não como código. Isso é absolutamente seguro.
  • \n
\n\n

Para ativar o modo nativo, desative a emulação:

\n\n
$pdo->setAttribute(PDO::ATTR_EMULATE_PREPARES, false);\n
\n\n

Agora a consulta é enviada ao servidor em duas etapas. Mesmo que o invasor envie ' OR '1'='1, o servidor MySQL ou PostgreSQL simplesmente interpretará isso como um valor string, não como parte do código SQL. Mágica!

\n\n

Mas é tudo tão perfeito? Armadilhas do PDO

\n\n

Voltemos à pergunta do Stack Overflow. A resposta "sim, PDO prepared statements são suficientes" — mas com ressalvas. Aqui estão situações em que a proteção pode falhar.

\n\n

1. Identificadores dinâmicos (nomes de tabelas, colunas)

\n\n

Prepared statements funcionam apenas para valores (dados). Se você inserir o nome de uma tabela ou coluna, os placeholders não ajudarão:

\n\n
// Perigoso! O nome da tabela não pode ser passado via placeholder\n$stmt = $pdo->prepare("SELECT * FROM :table WHERE id = :id");\n$stmt->execute([':table' => $_GET['table'], ':id' => 1]); // Erro!\n
\n\n

O PDO simplesmente inserirá o nome da tabela como string, e a consulta quebrará. Se você usar emulação, o invasor pode enviar users; DROP TABLE users; --. Solução: lista branca de valores permitidos.

\n\n

2. Consultas LIKE e caracteres curinga

\n\n

Se você usar

Blogs

Book Recommendations