PHP e SQL Injection
07/11/06
Estava explicando para um amigo meu porque não se deve interpolar uma variável direto no SQL. O grande problema é que ao fazer desta maneira, seu sistema fica suscetível a SQL Injection.
Imagine que você faça algo como abaixo.
<?php
$query = "SELECT name, email, password FROM table WHERE field='".$_GET['field']."' LIMIT 1";
?>
Se alguém passar no parâmetro algo como ' OR 1=1 #, seu banco ficaria totalmente exposto, pois a string final seria modificada com uma condição que será sempre satisfeita.
SELECT name, email, password FROM table WHERE field = '' OR 1=1 #' LIMIT 1
Parabéns! Alguém acabou de pegar todos os registros de sua tabela. Para evitar esse tipo de ataque, é aconselhável usar a função mysql_real_escape_string. Ela faz o devido escape de caracteres potencialmente inseguros. A desvantagem é que você tem que fazer isso para cada um dos parâmetros que você vai passar, o que pode se tornar um pouco massante.
Então, passei para ele uma função que eu usava há algum tempo atrás.
<?php
/**
* @param string $query
* @param mixed $arg1, $arg2...$argN
*/
$QUERY = "";
function query($query)
{
global $QUERY;
$args = func_get_args();
$query = array_shift($args);
foreach ($args as $key => $arg) {
if (is_string($arg)) {
$args[$key] = mysql_real_escape_string($arg);
}
}
array_unshift($args, $query);
$query = call_user_func_array('sprintf', $args);
$QUERY = $query;
return mysql_query($query);
}
?>
Use a função acima da seguinte maneira:
<?php
$query = "SELECT name, email, password FROM table WHERE field='%s' LIMIT 1";
$resource = query($query, $_GET['field']);
while ($row = mysql_fetch_object($resource)) {
printf('<strong>%s:</strong> %s<br/>', $row->name, $row->email);
}
?>
Você pode passar quantos parâmetros precisar, seguindo as regras da função sprintf.
<?php
$query = "INSERT INTO table (amount, age, name) VALUES (%.2f, %d, '%s')";
$resource = query($query, 150, 27, 'Nando Vieira');
?>
Uma outra vantagem de usar a função acima é que se você esquecer de passar algum parâmetro que estava esperando receber, um erro será exibido. E de quebra você ganha uma variável global $QUERY com a última instrução executada.

Comentários #
A idéia, em si, é boa, mas não funcionará em versões do PHP inferiores a 4.3. Em todo caso, é possível criar funções do tipo "eh_um_numero_de_verdade()" e "eh_string_mesmo()", hehe, obviamente não com esses nomes esdrúxulos. Apesar de mais simples, é uma forma de garantir que, de onde deve vir um número, não venha uma string maliciosa, por exemplo.
A propósito, belo blog! Estou esperando a parte 2 do post sobre as extensões do Firefox.
Um abraço.
Excelente dica para os programadores menos precavidos.
Eu sempre usei uma outra rotina parecida com essa sua, mas acabei perdendo, mas já guardei essa sua na manga, sempre é bom ter essas coisas por perto qdo se está nesse mundo.
Obrigado,
Gean.
Fernando: no caso das versões inferiores você pode usar a função mysql_escape_string, apesar de obsoleta. A segunda parte do artigo de extensões já está sendo escrito!
Olá Nando, o que acha disso?
function antiSqlInje($dado) {
$dado = strip_tags($dado);
$dado = trim($dado);
$dado = get_magic_quotes_gpc() == 0 ? addslashes($dado) : $dado;
$dado = preg_replace("@(--|\#|;)@s", "", $dado);
return $dado;
}
é funcional?
João: ela é bem genérica, visto que não necessariamente quero matar as tags ou retirar todos os espaços. No caso do GPC, você está supondo que o $dado veio através de POST, COOKIE ou GET. Na expressão regular, você pode matar algo como "-- texto --" que no markdown é substituído por — teste —. Mas se você não cai em nenhum dos casos que falei, serve muito bem.
Deixe um comentário