O que são Cross-Site Request Forgery e como proteger o seu site
Agora que já todos protegemos os nossos sites contra ataques Cross-Site-Scripting (XSS) está na altura de prestarmos atenção a uma das ameaças que continua a afectar uma grande percentagem da web.
Os ataques Cross-Site-Scripting (XSS) tiram partido das falhas na forma como um determinado site processa os conteúdos inseridos por um utilizador em formulários, URLs e até mesmo nos cabeçalhos dos pedidos HTTP. Explorando esses erros na validação de conteúdos, um atacante pode introduzir Javascript malicioso na base de dados do site. Quando o site atacado exibe essa informação a um utilizador normal, o Javascript malicioso é executado no browser do utilizador normal podendo assim aceder a informações privadas como Cookies por exemplo. Em sites que utilizam apenas Cookies como meio de autenticação, um ataque XSS bem sucedido, permite a um utilizador malicioso ganhar controlo sob a identidade de um utilizador vítima. Sem querer entrar em demasiados detalhes sobre o tema, e deixando a oportunidade aberta para um artigo futuro mais extenso e detalhado, não posso deixar de alertar para a importância de filtrar correctamente os conteúdos antes de os aceitar ou os mostrar aos seus utilizadores.
Os ataques Cross-Site Request Forgery não precisam de injectar nenhum código malicioso no seu site e são tão ou mais eficazes que os ataques XSS, podendo produzir efeitos catastróficos. Isto é de facto muito importante, e este tipo de vulnerabilidades não deve ser de todo ignorada.
Hoje em dia existe a tendência de dar muita importância aos processos de login num site e criam-se inúmeros mecanismos de segurança em torno do mesmo, desde os já conhecido teclados virtuais até aos cartões matriz únicos por utilizador. No entanto, repetidamente se esquece que, na verdade, é depois de fazer login que um utilizador está mais vulnerável.
É exactamente esse aspecto que os ataques Cross-Site-Request exploram. Incluindo um link ou script numa página, de forma a fazer um pedido HTTP a um site no qual a vítima se encontra autenticada, um atacante pode levar um utilizador vítima a executar acções nesse site sem o seu consenso ou conhecimento.
Vejamos o seguinte exemplo: O Pedro escreve um blog onde diariamente coloca novos posts. Infelizmente o Pedro utiliza uma versão antiga blog que ainda aceita posts via pedidos GET:
http://www.aventurasdopedro.pt/admin/post_blog.php?title=Emprego&content=Mais um dia passado no emprego
O André, que conhece os hábitos do Pedro, cria uma página com a seguinte imagem:
<img src="http://www.aventurasdopedro.pt/admin/post_blog.php?title=Foste vitima de um ataque XSRF&content=" width="0" height="0"/>Ao aceder a esta página, o browser do Pedro envia os Cookies contendo uma autenticação válida para o [post_blog.php] que, não conseguindo diferenciar o pedido forjado de um pedido válido, vai criar post não autorizado no blog.
Mesmo depois do Pedro actualizar o seu blog para apenas aceitar pedidos POST, o seu blog continua vulnerável pois o André pode simplesmente criar uma página com um formulário escondido que é automaticamente submetido quando a página é visualizada:
<html> <body onload=”document.hackPedro.submit()”> <form name=”hackPedro” action=” http://www.aventurasdopedro.pt/admin/post_blog.php” method=”POST” target=”if” style=”display:none”> <input type=”hidden” name=”title” value=”Mais uma vez, foste vitíma de um ataque XSRF” /> <input type=”hidden” name=”content” value=”…” /> </form> <iframe name=”if” width=”0” height=”0”></iframe> </body> </html>
A página submete um POST para o scrip [post_blog.php] que mais uma vez não vai conseguir detectar que se trata de um pedido forjado.
Este pequeno exemplo demonstra um pouco as potencialidades deste ataque, mas se esta pequena contenda entre amigos não é motivo suficiente para o fazer saltar da cadeira, passemos então a um exemplo mais sério e uma demonstração de como esta pequena falha de segurança se pode transformar num worm e em poucas horas encher completamente uma base de dados com spam.
Vejamos o seguinte sistema fictício de email [bombamail.pt] (web2.0 claro), que faz uso de um pedido Ajax para visualizar a lista de contactos no site.
O script [bombamail.pt/get_contacts.php] retorna um Objecto Javascript (JSON) contendo a seguinte informação:
[{"id":1,"endereco":"pedro@bombamail.pt"},{"id":2,"endereco":"joao.pestana@bombamail.pt"}]
Um atacante pode assim criar uma página com um Javascript malicioso que captura este objecto ganhando assim acesso aos contactos do utilizador vítima:
<html> <head> <script> /* Esta funcao faz o override to constructor generico dos Objectos Javascript Cada vez que a variavel "endereco" e' definida, a funcao "roubarContacto" e' executada e como essa variavel e' a ultima nos Objectos devolvidos pelo script [bombamail.pt/get_contacts.php] ao executar a funcao uma vez, conseguimos ter accesso a todo o Objecto */ function Object() { this.endereco setter = roubarContacto } function roubarContacto(endereco) { var div = document.getElementById("DetalhesRoubados"); div.innerHTML = div.innerHTML + "<br /> ID:" + this['id'] + " (" + endereco + ") "; } </script> </head> <body> <div id="DetalhesRoubados"> Detalhes roubados: </div> </body> <script src="http://bombamail.pt/get_contacts.php"></script> </html>
Neste exemplo, o atacante, utiliza a tag script para fazer um pedido ao [get_contacts.php] e incluir assim o objecto Javascript (JSON). Não conseguido identificar que se trata de uma página forjada, o browser envia os Cookies devidamente autenticados produzindo assim uma resposta normal. De seguida, e tirando partido da natureza inerente ao Javascript, este script malicioso faz o override do construtor geral a todos os objectos de forma a definir uma função setter para a variável “endereco” que ao ser executada permite o acesso a todo o conteúdo do objecto, produzindo finalmente o seguinte resultado:
Detalhes roubados:
ID:1 (blaaa@mooo.com) ID:2 (average@joe.com)
A partir do momento em que o atacante ganha acesso a lista de contactos, transformar o ataque num worm com a potencialidade de espalhar-se completamente por todos os utilizadores do serviço [bombamail.pt] é bastante simples. O atacante pode simplesmente alterar o Javacript para enviar a lista de contactos para uma segunda página maliciosa (utilizado Ajax por exemplo) que por sua vez envia um email contendo um link para a página maliciosa inicial a cada contacto na lista espalhando assim o ataque por vários utilizadores e, potencialmente, para todos os utilizadores no sistema.
Apesar deste tipo de ataques poder ser um pesadelo para qualquer webmaster, na verdade é possível preveni-los com apenas algumas medidas de segurança.
O desafio é então garantir que todas as acções efectuados num site são iniciadas por um utilizador real e não por um script malicioso.
Protegendo os seus objectos JSON
Uma das formas de proteger os pedidos Ajax que utilizam JSON como meio de transmitir a informação é introduzir um ciclo infinito no início do objecto e estabelecer um passo de pré-processamento (filtrar o ciclo infinito) antes de utilizar a informação. Por exemplo:
while(1); [{"id":1,"endereco":"pedro@bombamail.pt"},{"id":2,"endereco":"joao.pestana@bombamail.pt"}]
Qualquer tentativa de incluir este objecto numa página maliciosa utilizando a tag script fica imediatamente impossibilitada pois o browser vai ficar bloqueado num ciclo infinito antes sequer de instanciar o objecto JSON.
O método da dupla submissão de Cookies
Este método funciona apenas para pedidos Ajax e consiste em enviar os Cookies como parte do POST ou GET comparando posteriormente no servidor os Cookies recebidos na variável $_COOKIE com a informação recebida nas variáveis $_POST ou $_GET.
Exemplo para pedidos POST:
[get_contacts.php] corrigido para utilizar a validação no método de dupla submissão de Cookies:
$cookies = array(); foreach($_COOKIE as $key => $value) $cookies[] = $key ."=". $value; $pedido_valido = ( implode("; ", $cookies) == $_POST['cookie'] ); if($pedido_valido) echo "OK :)"; else echo "Pedido Invalido";
O método do Token único
Dos vários métodos existentes, este é o meu preferido pois pode ser utilizado em todas as situações (formulários, URLs, Ajax, etc). Apesar de requerer algum esforço inicial para proteger um site por completo, este método é simples, rápido e eficiente.
A ideia é então adicionar um Token único, aos links ou formulários que pretendemos proteger, efectuando a validação no servidor antes de aceitar o pedido.
Este Token deve ser gerado em função dos Cookies do utilizador e de um segredo conhecido apenas no servidor, por exemplo:
function security_token() { return md5($_COOKIE[‘auth] . “SEGREDO”); }
Incluir este Token em links ou formulários:
<a href=”/logout.php?token=<?php echo security_token() ?>”>Sair</a>
<form action=”/enviar_email.php” method=”POST”> <input type=”hidden” name=”token” value=”<?php echo security_token() ?>”>” /> ..... </form>
Antes de executar as páginas [/logout.php] ou [/enviar_email.php] é possível validar o Token e perceber se se trata de um pedido forjado:
if($_GET[‘token’] != security_token()) { header(“HTTP/1.0 403 Forbidden”); exit(); }
ou
if($_POST[‘token’] != security_token()) { header(“HTTP/1.0 403 Forbidden”); exit(); }
Desta forma é possível garantir ambas as acções só podem ser iniciadas por um utilizador autenticado e não por um script malicioso numa página forjada pois cada Token é único por cada utilizador.
Na eventualidade de um atacante conseguir obter o Token dum utilizador, qualquer ataque será sempre contido visto que poderá apenas afectar o utilizador cujo o token foi roubado.
Este método pode ser melhorado introduzindo uma componente dinâmica ao segredo, utilizando, por exemplo, a data do último login criando assim um Token diferente de cada vez que o utilizador entra no site.
0 Responses to “Segurança na web. Como proteger o seu site contra XSRF”