Você está na página 1de 4

Evite SQL Injection usando Prepared Statements no PHP

Uma das maiores vulnerabilidades de sites, a injeo de SQL (SQL Injection) tambm, no caso do PHP, uma das mais fceis de prevenir. Infelizmente, muitos no tomam as devidas precaues e acabam tendo os seus dados comprometidos. Neste tutorial, irei demonstrar como trabalhar com prepared statements usando a extenso PDO do PHP. SQL Injection Antes de comear, vale a pena ilustrar como funciona um ataque tpico de SQL Injection:
// Temos esta consulta simples aonde os dados $username e $password vem de um formulrio preenchido pelo usurio $query = "SELECT * FROM tabela WHERE username = '$username'"; // Se no houver validao correta um usurio mal-intencionado poderia colocar algum cdigo SQL no lugar do username: $username = "' OR 1'"; // A consulta ficaria assim: $query = "SELECT * FROM tabela WHERE username = "'' OR 1"; // Como a expresso 'OR 1' sempre resulta em TRUE, a consulta retornaria os dados de todos os usurios no sistema

Convenhamos, o exemplo acima bobo, mas serve para mostrar a teoria por trs da tcnica de injeo de sql. Se ainda no se convenceu de que necessrio, veja um exemplo mais grave:
// comeamos com a mesma consulta $query = "SELECT * FROM tabela WHERE username = '$username'"; // desta vez, o cdigo inserido bem mal-intencionado mesmo... $username = "'; DELETE FROM tabela WHERE 1 OR username = '"; // a consulta final ficaria assim: $query = "SELECT * FROM tabela WHERE username = ''; DELETE FROM tabela WHERE 1 OR username = ''"; // ou seja, se executada, a consulta excluiria todos os registros da tabela

Validao sozinha no resolve! Voc pode estar questionando se uma boa validao j no resolveria o problema, j que em ambos exemplos validar para aceitar somente letras funcionaria para bloquear ambas

tentavas. Bom a resposta : SIM e NO! Por mais que a validao ajude, mesmo usando expresses regulares complexas, h meios de burl-la utilizando outros charsets e tcnicas maliciosas. Segurana nunca demais e no custa se prevenir para proteger os seus dados ou os dos seus clientes. O que so prepared statements? Nada mais so do que consultas "pr-prontas"... A diferena que em lugar das variveis voc coloca um placeholder (marcador de lugar) e na hora da consulta informa a ordem das variveis a serem substituidas. mais fcil de explicar com um exemplo!
// a interrogao vai no lugar da varivel $query = "SELECT * FROM tabela WHERE username = ?"; // para fazer com vrios parametros a mesma coisa $query = "SELECT * FROM tabela WHERE username = ? OR username = ?";

Depois, s informar o que vai no lugar dos respectivos '?' e a consulta estar protegida! Isto funciona porque ao prepararmos a consulta, avisamos ao MySQL (ou outro RDBMS que suporte prepared statements) como a consulta e exatamente aonde vo as variveis. Repare que nem precisamos mais colocar aspas em volta da varivel, pois ele j sabe que uma varivel e a trata de acordo. No PHP, a extenso MySQLi tambm suporta statements preparados, mas recomendo sempre utilizar o PDO pois ele facilita a migrao para outros bancos, alm de oferecer uma API concisa entre eles. Como Funciona com PDO
// vamos partir do pressuposto que temos um objeto PDO instanciado e devidamente configurado e iremos trabalhar com a mesma consulta dos exemplos anteriores $query = "SELECT * FROM tabela WHERE username = ?"; // o mtodo PDO::prepare() retorna um objeto da classe PDOStatement ou FALSE se ocorreu algum erro (neste caso use $pdo->errorInfo() para descobrir o que deu errado) $stmt = $pdo->prepare($query); // agora que temos o statement preparado, precisamos "bindar" a varivel $username = "fulano"; // utilizamos o mtodo PDOStatement::bindValue() que aceita como parmetros a posio do ? que a varivel ir substituir (a primeira 1) e a prpria varivel $stmt->bindValue(1, $username); // executamos o statement $ok = $stmt->execute(); // agora podemos pegar os resultados (partimos do pressuposto que no

houve erro) $results = $stmt->fetchAll(PDO::FETCH_ASSOC);

Parece meio chato, ter que escrever tanto cdigo a mais para executar uma simples consulta e se a questo da segurana no for motivo sufiente, que tal este: statements preparados so mais rpidos que consultas normais! Especialmente se a mesma consulta for executada diversas vezes durante um request (mudando ou no as variveis). Como o assunto extenso, incluirei algumas informaes adicionais para esclarecer alguns itens, se no precisar de tanto detalhe tcnico pode pular ao final! Informaes Complementares

H outro mtodo para fazer "bind" das variveis: o PDO::bindParam(), a sntaxe exatamente a mesma mas ele recebe a varivel por referncia, enquanto PDO::bindValue() recebe por valor.

Tanto o bindValue() quanto o bindParam() aceitam um terceiro parmetro opcional que o tipo da varivel (PDO::PARAM_STR, PDO::PARAM_INT, etc)

Se no gostou da sintaxe de colocar varias interrogaes e depois substituir as variveis de acordo com a posio da interrogao, tambm possvel utilizar placeholders nomeados:
// em vez da interrogao utilizamos uma palavra prefixada de um ":" $query = "SELECT * FROM tabela WHERE username = :usuario OR username = :administrador"; // na hora de fazer bind fica mais claro qual varivel vai aonde $stmt->bindValue(":usuario", $username); $stmt->bindValue(":administrador", "admin"); // este mtodo tambm facilita fazer binds dentro de loops foreach aonde o placeholder a chave e a varivel o valor foreach($variaveis as $k => $v){ $stmt->bindValue(":$k", $v); }

E isto, chegamos ao final! Espero que o artigo tenha ajudado e lembre-se, pular estes simples passos o tpico barato que sai caro!

Você também pode gostar