Impedir ataques CSRF (solicitação intersite forjada) em ASP.NET aplicativo MVC

CSRF (Solicitação Intersite Forjada) é um ataque em que um site mal-intencionado envia uma solicitação para um site vulnerável em que o usuário está conectado no momento

Aqui está um exemplo de um ataque CSRF:

  1. Um usuário faz logon usando www.example.com a autenticação de formulários.

  2. O servidor autentica o usuário. A resposta do servidor inclui um cookie de autenticação.

  3. Sem fazer logoff, o usuário visita um site mal-intencionado. Este site mal-intencionado contém o seguinte formulário HTML:

    <h1>You Are a Winner!</h1>
      <form action="http://example.com/api/account" method="post">
        <input type="hidden" name="Transaction" value="withdraw" />
        <input type="hidden" name="Amount" value="1000000" />
      <input type="submit" value="Click Me"/>
    </form>
    

    Observe que a ação de formulário é posta no site vulnerável, não no site mal-intencionado. Esta é a parte "entre sites" do CSRF.

  4. O usuário clica no botão Enviar. O navegador inclui o cookie de autenticação com a solicitação.

  5. A solicitação é executada no servidor com o contexto de autenticação do usuário e pode fazer qualquer coisa que um usuário autenticado tenha permissão para fazer.

Embora este exemplo exija que o usuário clique no botão de formulário, a página mal-intencionada pode executar facilmente um script que envia o formulário automaticamente. Além disso, o uso de SSL não impede um ataque CSRF, pois o site mal-intencionado pode enviar uma solicitação de "https://".

Normalmente, ataques CSRF são possíveis em sites que usam cookies para autenticação, pois os navegadores enviam todos os cookies relevantes para o site de destino. No entanto, os ataques CSRF não se limitam à exploração de cookies. Por exemplo, a autenticação Básica e Digest também são vulneráveis. Depois que um usuário fizer logon com a autenticação Básica ou Digest. o navegador envia automaticamente as credenciais até que a sessão termine.

Tokens antifalsificação

Para ajudar a evitar ataques CSRF, ASP.NET MVC usa tokens antifalsificação, também chamados de tokens de verificação de solicitação.

  1. O cliente solicita uma página HTML que contém um formulário.
  2. O servidor inclui dois tokens na resposta. Um token é enviado como um cookie. O outro é colocado em um campo de formulário oculto. Os tokens são gerados aleatoriamente para que um adversário não possa adivinhar os valores.
  3. Quando o cliente envia o formulário, ele deve enviar os dois tokens de volta para o servidor. O cliente envia o token de cookie como um cookie e envia o token de formulário dentro dos dados do formulário. (Um cliente do navegador faz isso automaticamente quando o usuário envia o formulário.)
  4. Se uma solicitação não incluir ambos os tokens, o servidor não permitirá a solicitação.

Aqui está um exemplo de um formulário HTML com um token de formulário oculto:

<form action="/Home/Test" method="post">
    <input name="__RequestVerificationToken" type="hidden"   
           value="6fGBtLZmVBZ59oUad1Fr33BuPxANKY9q3Srr5y[...]" />    
    <input type="submit" value="Submit" />
</form>

Os tokens antifalsificação funcionam porque a página mal-intencionada não pode ler os tokens do usuário devido a políticas de mesma origem. (As políticas de mesma origem impedem que documentos hospedados em dois sites diferentes acessem o conteúdo um do outro. Portanto, no exemplo anterior, a página mal-intencionada pode enviar solicitações para example.com, mas não pode ler a resposta.)

Para evitar ataques CSRF, use tokens antifalsificação com qualquer protocolo de autenticação em que o navegador envia credenciais silenciosamente após o usuário fazer logon. Isso inclui protocolos de autenticação baseados em cookie, como autenticação de formulários, bem como protocolos como autenticação Básica e Digest.

Você deve exigir tokens antifalsificação para quaisquer métodos não seguros (POST, PUT, DELETE). Além disso, verifique se os métodos seguros (GET, HEAD) não têm efeitos colaterais. Além disso, se você habilitar o suporte entre domínios, como CORS ou JSONP, até mesmo métodos seguros como GET serão potencialmente vulneráveis a ataques CSRF, permitindo que o invasor leia dados potencialmente confidenciais.

Tokens antifalsificação no ASP.NET MVC

Para adicionar os tokens anti-falsificação a uma página razor, use o método auxiliar HtmlHelper.AntiForgeryToken :

@using (Html.BeginForm("Manage", "Account")) {
    @Html.AntiForgeryToken()
}

Esse método adiciona o campo de formulário oculto e também define o token de cookie.

Anti-CSRF e AJAX

O token de formulário pode ser um problema para solicitações AJAX, porque uma solicitação AJAX pode enviar dados JSON, não dados de formulário HTML. Uma solução é enviar os tokens em um cabeçalho HTTP personalizado. O código a seguir usa a sintaxe Razor para gerar os tokens e adiciona os tokens a uma solicitação AJAX. Os tokens são gerados no servidor chamando AntiForgery.GetTokens.

<script>
    @functions{
        public string TokenHeaderValue()
        {
            string cookieToken, formToken;
            AntiForgery.GetTokens(null, out cookieToken, out formToken);
            return cookieToken + ":" + formToken;                
        }
    }

    $.ajax("api/values", {
        type: "post",
        contentType: "application/json",
        data: {  }, // JSON data goes here
        dataType: "json",
        headers: {
            'RequestVerificationToken': '@TokenHeaderValue()'
        }
    });
</script>

Ao processar a solicitação, extraia os tokens do cabeçalho da solicitação. Em seguida, chame o método AntiForgery.Validate para validar os tokens. O método Validate gerará uma exceção se os tokens não forem válidos.

void ValidateRequestHeader(HttpRequestMessage request)
{
    string cookieToken = "";
    string formToken = "";

    IEnumerable<string> tokenHeaders;
    if (request.Headers.TryGetValues("RequestVerificationToken", out tokenHeaders))
    {
        string[] tokens = tokenHeaders.First().Split(':');
        if (tokens.Length == 2)
        {
            cookieToken = tokens[0].Trim();
            formToken = tokens[1].Trim();
        }
    }
    AntiForgery.Validate(cookieToken, formToken);
}