Предотвращение атак с подделкой межсайтовых запросов (CSRF) в ASP.NET приложении MVC

Подделка межсайтовых запросов (CSRF) — это атака, при которой вредоносный сайт отправляет запрос на уязвимый сайт, где пользователь в данный момент вошел в систему.

Ниже приведен пример атаки CSRF:

  1. Пользователь входит в www.example.com систему с помощью проверки подлинности с помощью форм.

  2. Сервер выполняет проверку подлинности пользователя. Ответ с сервера содержит файл cookie для проверки подлинности.

  3. Без выхода пользователь посещает вредоносный веб-сайт. Этот вредоносный сайт содержит следующую 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>
    

    Обратите внимание, что действие формы публикуется на уязвимом сайте, а не на вредоносном сайте. Это "межсайтовая" часть CSRF.

  4. Пользователь нажимает кнопку "Отправить". Браузер включает файл cookie проверки подлинности с запросом.

  5. Запрос выполняется на сервере с контекстом проверки подлинности пользователя и может делать все, что разрешено пользователю, прошедшему проверку подлинности.

Хотя в этом примере пользователю требуется нажать кнопку формы, вредоносная страница может так же легко запустить скрипт, который отправляет форму автоматически. Кроме того, использование SSL не предотвращает атаку CSRF, так как вредоносный сайт может отправить запрос "https://".

Как правило, атаки CSRF возможны на веб-сайты, использующие файлы cookie для проверки подлинности, так как браузеры отправляют все соответствующие файлы cookie на целевой веб-сайт. Однако атаки CSRF не ограничиваются использованием файлов cookie. Например, базовая и дайджест-проверка подлинности также уязвимы. После входа пользователя с помощью обычной или дайджест-проверки подлинности. браузер автоматически отправляет учетные данные до завершения сеанса.

Защита от подделки маркеров

Чтобы предотвратить атаки CSRF, ASP.NET MVC использует маркеры защиты от подделки, также называемые маркерами проверки запросов.

  1. Клиент запрашивает HTML-страницу, содержащую форму.
  2. Сервер включает в ответ два маркера. Один маркер отправляется в виде файла cookie. Другой объект помещается в скрытое поле формы. Маркеры создаются случайным образом, чтобы злоумышленник не может угадать значения.
  3. Когда клиент отправляет форму, он должен отправить оба маркера обратно на сервер. Клиент отправляет маркер файла cookie в виде файла cookie и отправляет маркер формы внутри данных формы. (Клиент браузера автоматически делает это, когда пользователь отправляет форму.)
  4. Если запрос не содержит оба маркера, сервер запрещает запрос.

Ниже приведен пример HTML-формы со скрытым маркером формы:

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

Защита от подделки маркеров работает, так как вредоносная страница не может прочитать маркеры пользователя из-за политик одного и того же источника. (Политики одного источника не позволяют документам, размещенным на двух разных сайтах, получать доступ к содержимому друг друга. Таким образом, в предыдущем примере вредоносная страница может отправлять запросы example.com, но не может прочитать ответ.)

Чтобы предотвратить атаки CSRF, используйте маркеры защиты от подделки с любым протоколом проверки подлинности, когда браузер автоматически отправляет учетные данные после входа пользователя. Сюда входят протоколы проверки подлинности на основе файлов cookie, такие как проверка подлинности на основе форм, а также такие протоколы, как обычная и дайджест-проверка подлинности.

Необходимо требовать маркеры защиты от подделки для любых небезопасных методов (POST, PUT, DELETE). Кроме того, убедитесь, что безопасные методы (GET, HEAD) не имеют побочных эффектов. Кроме того, если включить междомовую поддержку, например CORS или JSONP, то даже безопасные методы, такие как GET, потенциально уязвимы к атакам CSRF, что позволяет злоумышленнику считывать потенциально конфиденциальные данные.

Защита от подделки маркеров в ASP.NET MVC

Чтобы добавить маркеры защиты от подделки на страницу Razor, используйте вспомогательный метод HtmlHelper.AntiForgeryToken :

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

Этот метод добавляет скрытое поле формы и задает маркер файла cookie.

Защита от CSRF и AJAX

Маркер формы может быть проблемой для запросов AJAX, так как запрос AJAX может отправлять данные JSON, а не данные html-формы. В этом случае можно отправлять токены в настраиваемый заголовок HTTP. Следующий код создает токены с использованием синтаксиса Razor, а затем добавляет их в запрос AJAX. Маркеры создаются на сервере путем вызова 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>

При обработке запроса извлеките токены из его заголовка. Затем вызовите метод AntiForgery.Validate для проверки маркеров. Метод Validate создает исключение, если маркеры недопустимы.

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