Как стать автором
Обновить

Передача AntiForgeryToken в каждый AJAX POST реквест, генерируемый Ajax.ActionLink

Этот текст описывает приём, полезный при создании Ajax UI на базе библиотек Microsoft, поставляемых вместе с ASP.NET MVC.

Хорошей практикой является валидирование всех POST реквестов с помощью AntiForgeryToken. Это легко сделать, генерируя его с помощью вызова хелпера Html.AntiForgeryToken() в каждой форме при рендеринге. При этом, в контролере к экшенам, валидирующим этот токен, добавляется атрибут ValidateAntiForgeryTokenAttribute. Всё хорошо, пока не используются методы взаимодействия с сервером, которые выполняют Ajax реквесты без явного создания форм. Например, такие, как созданные с помощью хелпера Ajax.ActionLink(). В случае использования хелпера Ajax.ActionLink форма создается динамически перед отправкой. Стандартного способа внедрить в её тело требуемый токен нет. В описываемом случае абсолютно все экшены, принимающие POST реквесты, валидируют AntiForgeryToken. Если ничего не предпринять, то реквесты, генерируемые кодом Ajax.ActionLink, будут отвергнуты сервером.

Код, о котором идет речь в тексте, генерируется на базе jQuery.Ajax.Unobtrusive и поэтому для кастомизации Ajax реквестов можно использовать стандартные jQuery методы $.ajaxSetup и $.ajaxPrefilter. В описываемом случае нужен именно $.ajaxPrefilter. С его помощью можно модифицировать каждый Ajax реквест перед его отправкой. Это позволит внедрять в каждый реквест информацию о токене. Ниже показано, как именно это делается. Сам токен следует где-то взять, чтобы добавить к реквесту. В общем случае, должен быть только один токен на странице. Это решается с помощью стандартного хелпера Html.AntiForgeryToken(). Его вызов можно разместить на странице _Layout, чтобы не делать это постоянно. Тут важно помнить, если кто-то вставит второй вызов этого хелпера, то приведёный код не сможет взять правильный вариант, т.к. берет самый первый. Ниже весь JS код, необходимый для работы описываемого подхода.

function GetAntiForgeryToken() {
    var tokenField = $("input[type='hidden'][name$='RequestVerificationToken']");
    if (tokenField.length == 0) {
        return null;
    } else {
        return {
            name: tokenField[0].name,
            value: tokenField[0].value
        };
    }
}
 
$.ajaxPrefilter(
    function(options, localOptions, jqXHR) {
        if (options.type !== "GET") {
            var token = GetAntiForgeryToken();
            if (token !== null) {
                if (options.data.indexOf("X-Requested-With") === -1) {
                    options.data = "X-Requested-With=XMLHttpRequest" + ((options.data === "") ? "" : "&" + options.data);
                 }
                 options.data = options.data + "&" + token.name + '=' + token.value;
             }
         }
     }
 );

Самое полезное в нём то, что очень быстро им закрыть существующую XSS дыру у сайта. Предположим на вашем сайте нет защиты POST реквестов от XSS. В таком случае требуется просто скопировать этот код в основной файл JS сайта и добавить атрибут [ValidateAntiForgeryToken] ко всем экшенам, принимающим POST реквесты. Так же, в файл _Layout добавить Html.AntiForgeryToken(). Теперь валидирование токена будет выполняться всегда при выполнении AJAX реквестов и разработчику не требуется постоянно помнить о добавлении токена в реквест.
Теги:
Хабы:
Данная статья не подлежит комментированию, поскольку её автор ещё не является полноправным участником сообщества. Вы сможете связаться с автором только после того, как он получит приглашение от кого-либо из участников сообщества. До этого момента его username будет скрыт псевдонимом.