Udostępnij za pośrednictwem


Zapobieganie atakom fałszerzowania żądań między witrynami (CSRF) w aplikacji MVC ASP.NET

Fałszowanie żądań między witrynami (CSRF) to atak polegający na tym, że złośliwa witryna wysyła żądanie do podatnej witryny, w której użytkownik jest już zalogowany.

Oto przykład ataku CSRF:

  1. Użytkownik loguje się do www.example.com przy użyciu uwierzytelniania formularzy.

  2. Serwer uwierzytelnia użytkownika. Odpowiedź z serwera zawiera plik cookie uwierzytelniania.

  3. Bez wylogowywania użytkownik odwiedza złośliwą witrynę internetową. Ta złośliwa witryna zawiera następujący formularz 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>
    

    Zwróć uwagę, że akcja formularza przesyła dane do witryny podatnej na zagrożenia, a nie do witryny złośliwej. Jest to część CSRF "cross-site".

  4. Użytkownik klika przycisk Prześlij. Przeglądarka zawiera plik cookie uwierzytelniania z żądaniem.

  5. Żądanie jest uruchamiane na serwerze z kontekstem uwierzytelniania użytkownika i może zrobić wszystko, co może zrobić uwierzytelniony użytkownik.

Mimo że w tym przykładzie użytkownik musi kliknąć przycisk formularza, złośliwa strona może równie łatwo uruchomić skrypt, który automatycznie przesyła formularz. Ponadto użycie protokołu SSL nie zapobiega atakowi CSRF, ponieważ złośliwa witryna może wysłać żądanie "https://".

Zazwyczaj ataki CSRF są możliwe w przypadku witryn internetowych, które używają plików cookie do uwierzytelniania, ponieważ przeglądarki wysyłają wszystkie odpowiednie pliki cookie do docelowej witryny sieci Web. Jednak ataki CSRF nie są ograniczone do wykorzystywania plików cookie. Na przykład, uwierzytelnianie podstawowe i typu Digest jest również podatne. Po zalogowaniu się przez użytkownika przy użyciu uwierzytelniania podstawowego lub zbiorczego. przeglądarka automatycznie wysyła poświadczenia do momentu zakończenia sesji.

Tokeny ochrony przed fałszerzami

Aby zapobiec atakom CSRF, ASP.NET MVC używa tokenów przeciw fałszerzowaniu, nazywanych również tokenami weryfikacji żądań.

  1. Klient żąda strony HTML zawierającej formularz.
  2. Serwer zawiera dwa tokeny w odpowiedzi. Jeden token jest wysyłany jako plik cookie. Druga jest umieszczana w ukrytym polu formularza. Tokeny są generowane losowo, aby przeciwnik nie mógł odgadnąć wartości.
  3. Gdy klient prześle formularz, musi wysłać oba tokeny z powrotem do serwera. Klient wysyła token pliku cookie jako plik cookie i wysyła token formularza wewnątrz danych formularza. (Klient przeglądarki automatycznie wykonuje to po przesłaniu formularza przez użytkownika).
  4. Jeśli żądanie nie zawiera obu tokenów, serwer nie zezwala na żądanie.

Oto przykład formularza HTML z ukrytym tokenem formularza:

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

Tokeny ochrony przed fałszerzowaniem działają, ponieważ złośliwa strona nie może odczytać tokenów użytkownika ze względu na zasady tego samego źródła. (Zasady tego samego źródła uniemożliwiają dostęp do zawartości dokumentów hostowanych w dwóch różnych witrynach. W poprzednim przykładzie złośliwa strona może wysyłać żądania do example.com, ale nie może odczytać odpowiedzi).

Aby zapobiec atakom CSRF, użyj tokenów ochrony przed fałszerzami z dowolnym protokołem uwierzytelniania, w którym przeglądarka dyskretnie wysyła poświadczenia po zalogowaniu użytkownika. Obejmuje to protokoły uwierzytelniania oparte na plikach cookie, takie jak uwierzytelnianie formularzy, a także protokoły, takie jak uwierzytelnianie podstawowe i szyfrowane.

Należy wymagać tokenów ochrony przed fałszerzami dla wszelkich metod niezabezpieczonych (POST, PUT, DELETE). Upewnij się również, że bezpieczne metody (GET, HEAD) nie mają żadnych skutków ubocznych. Ponadto jeśli włączysz obsługę między domenami, taką jak CORS lub JSONP, nawet bezpieczne metody, takie jak GET, są potencjalnie narażone na ataki CSRF, co umożliwia atakującemu odczytywanie potencjalnie poufnych danych.

Tokeny ochrony przed fałszerzami w ASP.NET MVC

Aby dodać tokeny chroniące przed fałszerstwem do strony Razor, użyj metody pomocnika HtmlHelper.AntiForgeryToken :

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

Ta metoda dodaje pole ukrytego formularza, a także ustawia token pliku cookie.

Anti-CSRF i AJAX

Token formularza może być problemem dla żądań AJAX, ponieważ żądanie AJAX może wysyłać dane JSON, a nie dane formularza HTML. Jednym z rozwiązań jest wysłanie tokenów w niestandardowym nagłówku HTTP. Poniższy kod używa składni Razor do generowania tokenów, a następnie dodaje tokeny do żądania AJAX. Tokeny są generowane na serwerze przez wywołanie 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>

Podczas przetwarzania żądania wyodrębnij tokeny z nagłówka żądania. Następnie wywołaj metodę AntiForgery.Validate , aby zweryfikować tokeny. Metoda Validate zgłasza wyjątek, jeśli tokeny są nieprawidłowe.

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);
}