É muito comum quando se estuda uma linguagem não se dar principal foco ao desenvolvimento em segurança, mas este, em ambientes de produção é um dos factores decisivos do sucesso de uma aplicação e da confiança por parte dos utilizadores.
Neste artigo vai ser abordada uma forma de defesa relativa a possíveis ataques de SQL Injection em PHP utilizando o SGDB MySQL, e com recurso a Prepared Statements.
Existe suporte oficial a várias bases de dados: CUBRID, MS SQL Server, Firebird/Interbase, IBM, Informix, MySQL, MS SQL Server, Oracle, ODBC e DB2, PostgreSQL, SQLite, 4D [print_r(PDO::getAvailableDrivers());]
Vantagens e Desvantagens
Vantagens:
i. Previne SQL Injection
ii. Melhora a performance (pode só ser notado quando usado em grandes plataformas)
iii. Mais simples de escrever e ler
Desvantagens e Limitações:
i. Limitado a: SELECT, INSERT, UPDATE, REPLACE, DELETE, e CREATE TABLE
ii. Placeholders apenas para valores e nunca para nomes de tabelas ou colunas
iii. Quando precisamos de utilizar o método bind_result no escopo de uma classe e não sabemos à partida quantas variáveis vão ser passadas. Problema analisado por Jeffrey Way @ NetTuts (http://net.tutsplus.com/tutorials/php/the-problem-with-phps-prepared-statements/)
addslashes vs mysql_real_escape_string vs Prepared Statements
As funções addslashes e mysql_real_escape_string não serão objecto de estudo neste artigo, será apenas faladas as vulnerabilidades que não acontecem com o uso de Prepared Statements de modo a mostrar que esta última alternativa é de facto a mais segura.
O Chris Shiflett (http://shiflett.org/archive/184) já analisou as vulnerabilidades das addslashes relativas ao abuso de caracteres multibyte.
A função mysql_real_escape_string não é muito diferente da addslashes ao nível do tipo de vulnerabilidade, na verdade a sua vantagem reside mesmo em ter em conta o character set a ser utilizado conseguindo determinar como analisar a informção passada. Contudo continuam a existir vulnerabilidades, por exemplo com a utilização da codificação GBK e da sequ?ncia 0xbf27 (¿’) que passada nesta função não terá o resultado pretendido de ¿\’ . (POC http://ilia.ws/archives/103-mysql_real_escape_string-versus-Prepared-Statements.html )
Prepared Statements em Uso
Vejamos então um exemplo bastante simples…
$login = $_GET['login']
$query = "SELECT * FROM registos WHERE login = '$login'";
em que /?login=’ OR 1′;
Assim sendo
A query ficará algo do género:
$query = "SELECT * FROM registos WHERE login = '' OR 1";
O que resultaria num retorno de todas as informações da base de dados.
Se neste caso especifico fosse utilizado o método de destaque deste artigo: Prepared Statements, teriamos de colocar um placeholder (para marcar o local onde serão inseridos os dados a introduzir / consultar na base de dados, isto faz com que nos locais onde estão colocados os placeholders não sejam feitos pedidos à base de dados ou alteração ao pedido em questão. É aqui que reside a força deste método.
Existem duas formas de usar placeholders tal como referido no Manual do PHP.net
Forma #1:
$stmt = $dbh->prepare("INSERT INTO REGISTRY (name, value) VALUES (:name, :value)");
$stmt->bindParam(':name', $name);
$stmt->bindParam(':value', $value);
Forma #2:
$stmt = $dbh->prepare("INSERT INTO REGISTRY (name, value) VALUES (?, ?)");
$stmt->bindParam(1, $name);
$stmt->bindParam(2, $value);
Voltando então ao exemplo anterior, a nossa expressão seria então:
$query = "SELECT * FROM registos WHERE login = ?";
Nota: Apesar da extensão MySQLi também suportar prepared statements é aconselhável a utilização da extensão PDO (PHP Data Objects)
$login = "joaoppereira";
$query = "SELECT * FROM registos WHERE login = ?";
$stmt = $pdo->prepare($query);
$stmt->bindValue(1, $login, PDO::PARAM_STR);
$ok = $stmt->execute();
$results = $stmt->fetchAll(PDO::FETCH_ASSOC);
A função bindValue dos PDOStatement permite simples validações de tipo de dados no terceiro parâmetro da função bindValue(parametro, valor, tipo_dado), com as seguintes opções:
- PDO::PARAM_STR - PDO::PARAM_INT - PDO::PARAM_BOOL - PDO::PARAM_NULL
Este foi só um artigo introdutório a este tema, existe agora imenso material no Manual do PHP.net e um pouco por toda a internet relativamente a esta temática para os que quiserem, e bem, aprofundar os conhecimentos relativos a Prepared Statements e PDO.
Bibliografia:
– http://php.net/manual/en/pdo.prepared-statements.php
– http://www.php.net/manual/en/class.pdostatement.php
– http://www.php.net/manual/en/pdo.constants.php
– http://www.php.net/manual/en/pdo.drivers.php
– http://net.tutsplus.com/tutorials/php/the-problem-with-phps-prepared-statements/
Estava procurando post sobre SQL Injection, gostei da relação de links que você colocou, valeu!!!
Trabalho há imenso tempo com PHP. Mas admiro muito conteúdo bem explicado e bem preparado. Abraços pra você meu amigo.