"Socorro! Meu mysql_real_escape_string() não funciona!"
Se você já pesquisou no Google por "SQL injection that gets around mysql_real_escape_string()", não está sozinho. Esta pergunta no Stack Overflow acumulou dezenas de milhares de visualizações e se tornou um clássico da segurança web. À primeira vista, você usa mysql_real_escape_string() e está protegido. Mas não. Hackers encontram brechas, e desenvolvedores ganham cabelos brancos.
Neste artigo, vamos analisar exatamente como as injeções SQL podem contornar o escape do mysql_real_escape_string() em PHP. Você verá exemplos reais de código, entenderá por que a "boa e velha" abordagem não funciona mais e aprenderá como se proteger de verdade. Vamos lá!
Por que mysql_real_escape_string() não é uma panaceia?
Para começar — um pouco de história. A função mysql_real_escape_string() surgiu na era do PHP 4 e 5, quando todos usavam a extensão mysql_*. Ela escapa caracteres especiais como aspas, barras invertidas e caracteres de controle. Mas o problema é que ela funciona apenas no contexto de uma única codificação.
Se a codificação do cliente e do servidor não coincidirem — começa o caos. E se você usar codificações multibyte (como GBK ou SJIS) — a função pode se tornar seu pior inimigo.
Exemplo clássico: ataque multibyte
Imagine que seu site usa a codificação GBK (chinesa), e você envia a string:
$_POST['name'] = "0xbf27"; // ' escapado ou apenas '?Em GBK, o caractere 0xbf27 é um caractere chinês válido. Mas se mysql_real_escape_string() "enxergar" o 0x27 (apóstrofo), ela o escapará com uma barra invertida: 0xbf5c27. O problema é que 0xbf5c também é um caractere válido em GBK! Resultado: o apóstrofo 0x27 permanece sem escape, e o hacker pode inserir seu código SQL.
// Código vulnerável$name = mysql_real_escape_string($_POST['name']);$query = "SELECT * FROM users WHERE name = '$name'";
// Se inserir: 0xbf27// A função retorna: 0xbf5c27// Em GBK isso é: caractere chinês + apóstrofo// A consulta se torna: SELECT * FROM users WHERE name = 'caract_chinês' OR 1=1 -- 'Voilà! A injeção foi bem-sucedida. E isso não é teoria — essas vulnerabilidades foram encontradas em projetos reais.
Vulnerabilidades típicas ao usar mysql_real_escape_string()
Vamos listar os erros mais comuns que permitem contornar o escape:
- Codificação incorreta — como no exemplo acima. Se o servidor acha que os dados estão em UTF-8, mas o cliente envia GBK, o escape falha.
- Uso de campos numéricos sem aspas — se você insere um número diretamente, o escape não funciona. Exemplo:
SELECT * FROM users WHERE id = $id— e o hacker escreve1 OR 1=1. - Consultas LIKE — o escape não trata os caracteres
%e_. É possível realizar Blind SQL injection através de padrões. - Escape múltiplo — se você chamar a função duas vezes, a barra invertida é duplicada e a proteção desaparece.
Exemplo: ataque a campo numérico
// Código vulnerável$id = mysql_real_escape_string($_GET['id']);$query = "SELECT * FROM products WHERE id = $id"; // Aspas não são necessárias!
// O hacker insere: 1 UNION SELECT username, password FROM users// A consulta se torna: SELECT * FROM products WHERE id = 1 UNION SELECT username, password FROM usersO escape aqui é inútil — não há aspas para escapar. O hacker simplesmente insere seu SQL diretamente.
Métodos modernos de proteção (que funcionam)
Chega de histórias de terror — vamos às soluções. Se você programa em PHP e quer esquecer as injeções SQL, aqui está sua lista de verificação:
1. Use PDO ou MySQLi com prepared statements
Isto é