Início‎ > ‎

Evitar ataques de CSRF

Nos últimos tempos, um problema recorrente e perigoso em aplicações web conhecido como CSRF (Cross-Site Request Forgery) tem sido muito comentado. Um ataque de CSRF consiste em inserir requisições em uma sessão já aberta pelo usuário. O processo básico é:
  1. o usuário se autentica em uma aplicação web alvo
  2. o usuário utiliza a mesma instância de browser para navegar em um site malévolo
  3. o site malévolo manipula o browser para que seja feita uma requisição à aplicação alvo
  4. como há uma sessão autenticada aberta para o usuário, a aplicação alvo executa a operação conforme a requisição recebida
Ou seja, o site malévolo consegue manipular a situação de forma a executar uma ação em nome do usuário. A manipulação do browser citada no item 3 acima pode ser feita de várias formas:
  1. se a aplicação alvo aceitar requisições via método GET, basta incluir uma tag <img> contendo todos os parâmetros necessários à aplicação alvo (qualquer outra tag que faça uma requisição HTTP funcionará da mesma forma);
  2. usando scripts incluídos na página, o que permite a execução de requisições GET ou POST.
Este ataque é extremamente difícil de ser detectado, dado que um identificador de sessão correto e válido será incluído na requisição recebida pela aplicação e a requisição partirá do mesmo browser e endereço IP das requisições legítimas. A aplicação web não como separar a requisição correspondente ao ataque das requisições legítimas.

Para evitar ataques de CSRF, a aplicação pode implementar as recomendações do item Usar corretamente POST e GET e também utilizar valores aleatórios a serem incluídos em cada formulário como um campo escondido, como mostra o exemplo abaixo:

<form ...>
<input type="hidden" name="csrf_value"
value="8dcb5e56904d9b7d4bbf333afdd154ca">


O valor aleatório incluído no campo csrf_value deve ser armazenado pela aplicação na sessão do usuário para que a aplicação verifique se a próxima requisição contém o valor correto. Assim, toda vez que a aplicação receber uma requisição via POST, deve-se verificar se o valor aleatório está presente e se corresponde ao valor esperado. Outra alternativa é incluir o valor aleatório em um cookie, o que evita a necessidade de manter informações na sessão do usuário.

No entanto, a menos que o browser implemente a same-origin policy, um script poderia capturar o valor aleatório no formulário em enviá-lo na requisição. Ou seja, uma proteção completa contra CSRF depende também do browser do usuário. A grande maioria dos browsers atuais implementa esta política, o que nos permite acreditar que a solução delineada acima apresenta proteção adequada contra CSRF.

O projeto CSRFGuard do OWASP é um filtro J2EE que insere os identificadores aleatórios nas páginas HTML e verifica estes identificadores automaticamente, já que após inserir na página, o identificador é também armazenado na sessão do usuário.

No Struts, é possível utilizar tokens aleatórios para implementar esta funcionalidade. Estes tokens foram projetados para evitar que uma requisição seja submetida mais de uma vez, mas, como implementam a funcionalidade descrita acima, podem ser usados para evitar CSRF. Os funções que o Struts implementa para a manipulação destes tokens são:
  1. saveToken(): gera um novo token e grava na sessão do usuário.
  2. isTokenValid(): verifica se o token é válido e corresponde aos dados presentes na sessão do usuário.
  3. resetToken(): invalida o token.
Com estas três funções, é possível implementar a proteção contra CSRF, conforme mostra o exemplo abaixo:

Classe Action que gera um novo token antes de chamar a página JSP:

public class LoadAction extends Action
{
    public ActionForward execute(ActionMapping mapping,ActionForm form,HttpServletRequest request,HttpServletResponse response)
    {
        ActionForward forward;
        forward=mapping.findForward("FirstPage");// página JSP onde será usado o token
        saveToken(request);

        return forward;
    }
}


O token deve então ser colocado num campo tipo hidden na página. O JSP abaixo mostra como fazer:

<%@ page import="org.apache.struts.action.Action"%>
<%@ page import="org.apache.struts.taglib.html.Constants"%>

<%@ taglib uri="/WEB-INF/struts-tiles.tld" prefix="tiles" %>
<%@ taglib uri="/WEB-INF/struts-html.tld" prefix="html" %>
<%@ taglib uri="/WEB-INF/struts-bean.tld" prefix="bean" %>
<%@ taglib uri="/WEB-INF/struts-logic.tld" prefix="logic" %>
<html>
<head> <title> First Page </title> </head>

<body>
<form name="MyForm" method="post" action="/dpsubm/getForm/submit.do">
<input type="text" name="name" >
<input type="hidden" name="<%= Constants.TOKEN_KEY %>"
value="<%= session.getAttribute(Action.TRANSACTION_TOKEN_KEY) %>" >
<input type="submit" value="submit">
</form>
</body>
</html>


A classe Action que recebe os dados do formulário deve verificar a validade do token antes de executar a sua lógica. O token deve ser invalidado para não ser reusado.

public class SubmitAction extends Action
{
    public ActionForward execute(ActionMapping mapping ,ActionForm form ,HttpServletRequest request,HttpServletResponse response)
    {
        ActionForward forward=mapping.findForward("submitForm");
        DupSubmitForm frm=(DupSubmitForm)form;

        if(isTokenValid(request))
        {

            System.out.println("frm.getName()"+frm.getName());
            resetToken(request);
        }
        else
        {
            System.out.println("frm.getName()"+frm.getName());
            System.out.println("Formulário já foi submetido. Recarregue a página e tente novamente.");

        }
        return forward;
    }
}


O Struts não implementa o controle dos tokens via cookies, apenas via campos tipo hidden.

Para outras técnicas que ajudam a evitar CSRF, veja os itens Usar corretamente POST e GET e Re-autenticar antes de operações críticas.

Fonte

ZELLER, William and FELTEN, Edward W. Cross-site Request Forgery: Exploitation and Prevention. Sptember 2008: http://www.freedom-to-tinker.com/sites/default/files/csrf.pdf

Java Developers Forum: http://x86.sun.com/thread.jspa?messageID=10340370
Comments