Introdução
POSTs e GETs, o que são
Na Web, quando uma página tem um formulário, os valores introduzidos nesses formulários podem ser enviados para o servidor de duas formas distintas.
A primeira é acrescentando no nomes dos campos e os respectivos valores ao URL. Este método chama-se GET, e tem várias vantagens e desvantagens.
Por um lado os GETs podem ser facilmente guardados em caches, sendo assim servidos mais rapidamente nas vezes seguintes que forem pedidos. Esta vantagem é também uma desvantagem, uma vez que isso significa que da próxima vez que o mesmo pedido for efectuado, este pode nunca chegar ao servidor, pois pode ser servidor por uma qualquer proxy que se encontre pelo caminho, a começar pelo proxy transparente que a maioria dos ISPs (telepac, netcabo, etc) têm.
Por outro lado, este método pode facilmente ser reconstruido e utilizado em links (o que acontece muitas vezes). É comum hoje vermos site com links do tipo index.php?id=12345. Estes links, na prática, estão apenas a tirar partido do sistema de GET pensado inicialmente para os formulários.
Os GETs, no entanto, não permitem tudo o que se pretende fazer actualmente com um formulário. O tamanho máximo dos URLs está limitado (o limite depende da implementação), e alguns tipos de dados, como os uploads de ficheiro, apenas podem ser feitos por POST.
Ao contrário dos GETs, nos POSTs os dados preenchidos nos formulários são enviados no corpo de pedido HTTP, pelo que não existem limites para a quantidade de dados enviados.
Ao contrário dos GETs, os POSTs nunca sao guardados em caches ou proxies, pois os proxies (normalmente) lêm apenas os headers dos pedidos HTTP, e por essa razão não conseguem destinguir dois pedidos feitos por POST. As definições do protocolo HTTP também definem que os POSTs não devem ser guardados em cache.
Utilizações dos dados
Os dados que os utilizadores colocam nos formulários, ou que são passados nos URLs são utilizados de diversas formas, sendo as mais comuns a alteração ou selecção de dados em bases de dados (que podem ser ficheiros em disco), o envio de emails, entre outras (o limite é a imaginação).
O exemplo mais simples talvez seja mesmo o parâmetro no URL, o index.php?id=1234 de que falamos acima. Normalmente este parâmetro será utilizado para selecionar numa base de dados um artigo, e mostrá-lo ao utilizador.
Assumindo que o index.php não faz mais nada, é suficiente o código seguinte para completar a tarefa pretendida (sem design, apenas o titulo e corpo do artigo, de uma tabela com 4 campos:
- id – o id do artigo
- title – o seu titulo
- body – o corpo do artigo
- publico – 1 se o artigo é publico, 0 se não o for.
<?php
$id=$_REQUEST['id'];
$db=mysql_connect('localhost','username','password');
mysql_select_db('sitedb',$db);
$query="SELECT title,body from stories WHERE id=$id AND publico=1";
if ($sel=mysql_query($query)) {
if ($res=mysql_fetch_assoc($sel)) {
echo "<h1>{$res['title']}</h1>{$res['body']}";
}
}
?>
Apenas algumas linhas de código, e um erro relativamente comum, especialmente em locais onde o ficheiro assim criado é utilizado “apenas” em listagens criadas automaticamente.
Sim, neste caso abrimos a porta a que qualquer artigo seja visivel, mesmo que o campo publico seja 0. Como?
Passando, por exemplo, no campo id o valor id=123 OR 1=0, o que irá fazer com que a nossa query passe então a ser:
SELECT title,body from stories WHERE id=123 OR 1=0 AND publico=1
Como o AND é verificado antes do OR, qualquer artigo com o id=123 passa a ser uma resposta válida para a query, uma vez que o conjunto 1=0 AND publico=1 é sempre falso, e consequentemente descartável, desde que id=123 seja verdadeiro.
Em php podemos evitar esta situação utilizando a função mysql_real_escape_string e colocando sempre os valores entre plicas (‘), como se segue:
<?php
$id=mysql_real_escape_string($_REQUEST['id']);
$db=mysql_connect('localhost','username','password');
mysql_select_db('sitedb',$db);
$query="SELECT title,body from stories WHERE id='$id' AND publico=1";
if ($sel=mysql_query($query)) {
if ($res=mysql_fetch_assoc($sel)) {
echo "<h1>{$res['title']}</h1>{$res['body']}";
}
}
?>
Duas pequenas alterações, mas que fazem toda a diferença. Deve colocar os valores entre plicas mesmo que os valores esperados sejam numéricos, ou então deve verificar se os valores têm realmente o formato esperado.
Conclusão
Isto é importante não apenas nos valores recebidos no URL, mas em todos os valores que sejam recebidos do utilizador, seja em URLs, seja em POSTs de formulários.
São pequenas alterações no seu código, mas que podem tornar a sua aplicação muito mais segura.
As linguagens e motores de base de dados que conheço têm um equivalente ao aqui apresentado. Se não existir implementado na que utiliza vale a pena perder uma ou duas horas, descobrir quais os caracteres que é preciso escapar no sistema que utiliza e implementar uma função que o faça, e que poderá reutilizar sempre que pretender.
Programar para web e manter os dados seguros não são necessariamente incompativeis. Basta seguir algumas regras simples. A maioria dos truques são muito simples, e mais simples ainda de impedir.
Pingback: Web a Sério » Programar para Web