jQuery Ajax-вызовы и Html.AntiForgeryToken ()

Я реализовал в своем приложении защиту от CSRF-атак после информации, которую я прочитал какое-то сообщение в блоге в Интернете. В частности, этот пост был драйвером моей реализации

В основном в этих статьях и рекомендациях говорится, что для предотвращения атаки CSRF любой должен реализовать следующий код:

1) Добавьте [ValidateAntiForgeryToken] в каждое действие, которое принимает команду POST Http

[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult SomeAction( SomeModel model ) {
}

2) Добавьте помощник <%= Html.AntiForgeryToken() %> внутри форм, который отправляет данные на сервер

<div style="text-align:right; padding: 8px;">
    <%= Html.AntiForgeryToken() %>
    <input type="submit" id="btnSave" value="Save" />
</div>

В любом случае в некоторых частях моего приложения я выполняю Ajax POST с jQuery на сервер, вообще не имея какой-либо формы. Это происходит, например, когда я разрешаю пользователю щелкнуть изображение, чтобы выполнить определенное действие.

Допустим, у меня есть таблица со списком занятий. У меня есть изображение в столбце таблицы с надписью «Отметить действие как завершенное», и когда пользователь нажимает на это действие, я выполняю Ajax POST, как в следующем примере:

$("a.markAsDone").click(function (event) {
    event.preventDefault();
    $.ajax({
        type: "post",
        dataType: "html",
        url: $(this).attr("rel"),
        data: {},
        success: function (response) {
            // ....
        }
    });
});

Как я могу использовать <%= Html.AntiForgeryToken() %> в этих случаях? Следует ли включать вызов помощника в параметр данных вызова Ajax?

Извините за длинный пост и большое спасибо за помощь

ИЗМЕНИТЬ:

Согласно ответу jayrdub, я использовал следующее способ

$("a.markAsDone").click(function (event) {
    event.preventDefault();
    $.ajax({
        type: "post",
        dataType: "html",
        url: $(this).attr("rel"),
        data: {
            AddAntiForgeryToken({}),
            id: parseInt($(this).attr("title"))
        },
        success: function (response) {
            // ....
        }
    });
});

person Lorenzo    schedule 02.11.2010    source источник
comment
David Hayden link теперь 404s, похоже, что он перенес свой блог на новую CMS, но не перенес весь старый контент.   -  person    schedule 02.01.2013


Ответы (19)


Я использую простую функцию js вроде этой

AddAntiForgeryToken = function(data) {
    data.__RequestVerificationToken = $('#__AjaxAntiForgeryForm input[name=__RequestVerificationToken]').val();
    return data;
};

Поскольку каждая форма на странице будет иметь одно и то же значение для токена, просто поместите что-то подобное на самую верхнюю главную страницу.

<%-- used for ajax in AddAntiForgeryToken() --%>
<form id="__AjaxAntiForgeryForm" action="#" method="post"><%= Html.AntiForgeryToken()%></form>  

Затем в вашем вызове ajax do (отредактировано в соответствии с вашим вторым примером)

$.ajax({
    type: "post",
    dataType: "html",
    url: $(this).attr("rel"),
    data: AddAntiForgeryToken({ id: parseInt($(this).attr("title")) }),
    success: function (response) {
        // ....
    }
});
person JeremyWeir    schedule 02.11.2010
comment
Хорошо, мне нравится инкапсуляция получения токена. - person jball; 02.11.2010
comment
Да, это приятно, так как много раз вы делаете сообщения ajax, у вас даже нет формы на странице, чтобы получить токен из - person JeremyWeir; 02.11.2010
comment
@jayrdub: спасибо за ответ! Где мне разместить маленькую функцию js? Я поместил внутрь js-файла с общей функцией и либо на главную страницу, но Chrome выдает мне ошибку, когда я его использую. Там написано Unexpected token (. Пожалуйста, просмотрите мой вопрос, чтобы узнать, как я его использую. - person Lorenzo; 02.11.2010
comment
@Lorenzo, поместите свои пользовательские данные в вызов AddAntiForgeryToken, например: data: AddAntiForgeryToken({ id: parseInt($(this).attr("title")) }), - person jball; 02.11.2010
comment
Джей, милый. полезная универсальная реализация. долгое время работал над краями чего-то (менее красивого). - person jim tollan; 10.04.2011
comment
@JeremyWeir С какой целью передать id AddAntiForgeryToken() - person painotpi; 27.02.2013
comment
Это просто пример того, как объединить токен защиты от подделки с любым объектом данных, который вы хотите отправить. Ничего особенного в этом нет, это просто соответствует примеру OP. - person JeremyWeir; 27.02.2013
comment
Повторите вышеприведенный комментарий, когда я реализовал его, в моем URL-адресе уже были параметры (например, / action / id = 444), поэтому я просто ничего не передавал в AddAntiForgeryToken ({}), и у меня это сработало. - person James in Indy; 21.05.2013
comment
Насколько плохой было бы использование ajaxSend или переопределение ajax, чтобы всегда добавлять data токен защиты от подделки? Может быть, добавить проверку, чтобы убедиться, что url предназначен для вашего сервера. - person ta.speot.is; 04.07.2013
comment
Будьте осторожны, если вы используете кеш вывода. - person Barbaros Alp; 02.11.2014
comment
что происходит, когда я делаю второй запрос ajax на той же странице без перезагрузки? не будет ли токен недействительным? - person Souhaieb Besbes; 28.12.2015
comment
@SouhaiebBesbes - токен проверки должен быть одинаковым для пользователя на всех страницах (он работает вместе с установленным файлом cookie и остается неизменным). Таким образом, не имеет значения, есть ли несколько запросов на страницу, все равно будет то же самое, если базовая страница перезагрузится. - person JeremyWeir; 28.12.2015
comment
@JeremyWeir, спасибо, оказывается, я неправильно понял, как работают эти токены - person Souhaieb Besbes; 28.12.2015
comment
это не будет работать, если на вашей странице включен кэш вывода - person sqlnewbie; 04.02.2016
comment
@sqlnewbie true, в зависимости от конфигурации outputcache. Кэширование одного и того же вывода для разных пользователей, безусловно, является обычным явлением, но можно настроить кэш вывода для одного и того же пользователя. - person JeremyWeir; 05.02.2016
comment
@JeremyWeir Требуется ли форма или вы можете просто сделать: @ Html.AntiForgeryToken () на странице? В моем случае на моей странице перечислена только группа продуктов в сетке, но я выполняю удаление с помощью запроса AJAX, поэтому мне нужен токен, но я не хочу добавлять пустую форму на мою страницу, если это не нужно. - person Blake Rivell; 18.04.2016
comment
@BlakeRivell Я сомневаюсь, что это необходимо для работы, форма предназначена для того, чтобы html оставался действительным. - person JeremyWeir; 19.04.2016
comment
Почему вместо этого он не добавлен в заголовок? Есть ли способ предпочтительнее? - person Murphybro2; 22.11.2017

Мне нравится решение 360Airwalk, но его можно немного улучшить.

Первая проблема заключается в том, что если вы создаете $.post() с пустыми данными, jQuery не добавляет заголовок Content-Type, и в этом случае ASP.NET MVC не может получить и проверить токен. Поэтому вы должны убедиться, что заголовок всегда присутствует.

Еще одним улучшением является поддержка всех HTTP-глаголов с содержимым: POST, PUT, DELETE и т. Д. Хотя вы можете использовать только POST в своем приложении, лучше иметь общее решение и проверять, что все данные, которые вы получаете с у любого глагола есть маркер защиты от подделки.

$(document).ready(function () {
    var securityToken = $('[name=__RequestVerificationToken]').val();
    $(document).ajaxSend(function (event, request, opt) {
        if (opt.hasContent && securityToken) {   // handle all verbs with content
            var tokenParam = "__RequestVerificationToken=" + encodeURIComponent(securityToken);
            opt.data = opt.data ? [opt.data, tokenParam].join("&") : tokenParam;
            // ensure Content-Type header is present!
            if (opt.contentType !== false || event.contentType) {
                request.setRequestHeader( "Content-Type", opt.contentType);
            }
        }
    });
});
person Bronx    schedule 24.08.2012
comment
+1 вы правы, я не думал о проблеме с пустым пост-звонком. спасибо за вклад. Вы были правы насчет того, что мы пока не используем delete / put в нашем проекте. - person 360Airwalk; 19.09.2012
comment
+1 за то, что избавил меня от необходимости добавлять функцию ко всем вызовам jQuery.Ajax - person Dragos Durlut; 09.10.2012
comment
+1 В качестве примечания для потомков в документации jQuery для .ajaxSend() указано, что начиная с jQuery 1.8, метод .ajaxSend () должен быть прикреплен только к документу. api.jquery.com/ajaxsend - person RJ Cuthbertson; 28.08.2014
comment
@Bronx Откуда взялось options, которое указано в последнем заявлении if? Спасибо. - person hvaughan3; 20.04.2016
comment
Остерегайтесь использовать это, если у вас есть несколько форм на странице. Вам нужно будет установить значение в beforeSend с более конкретным вызовом селектора, а не для документа. - person Dan; 11.09.2016
comment
Я не уверен, почему это так сложно, почему нельзя просто установить токен и сделать это с помощью одной строки кода, как здесь (если мы удалим условие if для POST) - erlend.oftedal.no/blog/static-118.html?blogid=118 - person PandaWood; 09.05.2019
comment
@PandaWood Мои основные моменты заключались в том, что 1) недостаточно охватывать только запросы POST, вы должны делать то же самое для всех запросов, имеющих любой контент, и 2) если вы прибегаете к методу скрытого поля, вы должны включить заголовок Content-Type. - person Bronx; 11.05.2019
comment
@PandaWood, мой ответ был модификацией другого ответа, который использовал скрытое поле формы для передачи токена, отсюда и сложности. Этот метод не требует изменений на сервере, поскольку ASP.NET MVC поддерживает скрытое поле формы «из коробки». Можно также использовать заголовок типа X-CSRF-Token, но для этого потребуется код проверки токена на сервере, как описано в docs.microsoft.com/en-us/aspnet/web-api/overview/security/. Так что решать вам. В теме уже есть еще один ответ, предлагающий вместо этого использовать заголовок. - person Bronx; 11.05.2019
comment
Спасибо @Bronx, да, я не понял, что ASP.NET не читает из заголовка (по умолчанию), поэтому это необходимо для того, что делает ValidateAntiForgeryTokenAttribute. - person PandaWood; 12.05.2019
comment
@Bronx как насчет форм с formData и processData / contentType, установленным в false? - person Angelo; 23.06.2020
comment
Неважно, проблема заключалась в добавлении фактического токена. Я использовал if (opt.data instanceof FormData) { opt.data.append("__RequestVerificationToken", securityToken); } else... - person Angelo; 24.06.2020

Я знаю, что есть много других ответов, но эта статья хороша и лаконична и заставляет вас проверять все свои HttpPosts, а не только некоторые из них:

http://richiban.wordpress.com/2013/02/06/validating-net-mvc-4-anti-forgery-tokens-in-ajax-requests/

Он использует заголовки HTTP вместо попытки изменить коллекцию форм.

Сервер

//make sure to add this to your global action filters
[AttributeUsage(AttributeTargets.Class)]
public class ValidateAntiForgeryTokenOnAllPosts : AuthorizeAttribute
{
    public override void OnAuthorization( AuthorizationContext filterContext )
    {
        var request = filterContext.HttpContext.Request;

        //  Only validate POSTs
        if (request.HttpMethod == WebRequestMethods.Http.Post)
        {
            //  Ajax POSTs and normal form posts have to be treated differently when it comes
            //  to validating the AntiForgeryToken
            if (request.IsAjaxRequest())
            {
                var antiForgeryCookie = request.Cookies[AntiForgeryConfig.CookieName];

                var cookieValue = antiForgeryCookie != null
                    ? antiForgeryCookie.Value 
                    : null;

                AntiForgery.Validate(cookieValue, request.Headers["__RequestVerificationToken"]);
            }
            else
            {
                new ValidateAntiForgeryTokenAttribute()
                    .OnAuthorization(filterContext);
            }
        }
    }
}

Клиент

var token = $('[name=__RequestVerificationToken]').val();
var headers = {};
headers["__RequestVerificationToken"] = token;

$.ajax({
    type: 'POST',
    url: '/Home/Ajax',
    cache: false,
    headers: headers,
    contentType: 'application/json; charset=utf-8',
    data: { title: "This is my title", contents: "These are my contents" },
    success: function () {
        ...
    },
    error: function () {
        ...
    }
});
person viggity    schedule 17.01.2014
comment
Атрибут из статьи, которую вы тоже связали, в сочетании с ответом Bronx является окончательным СУХИМ решением этой проблемы. - person TugboatCaptain; 02.04.2014
comment
Отличная находка. Я отредактировал ваш ответ, включив в него фрагменты кода, чтобы ответ стоял сам по себе, но я надеюсь, что люди прочитают и остальную часть статьи. Это кажется очень чистым решением. - person Tim Medora; 18.04.2014
comment
Спасибо, Тим, это отличная идея, очень неприятно, когда ссылка перестает работать и ответ становится бесполезным. Я начал делать это во всех своих новых ответах. - person viggity; 29.07.2014
comment
Это MVC, WebAPI или .NetCore? Я не могу найти правильные пространства имен для WebAPI 5 - person Myster; 20.12.2019

Я чувствую себя здесь опытным некромантом, но 4 года спустя в MVC5 это все еще проблема.

Чтобы правильно обрабатывать запросы ajax, токен защиты от подделки должен быть передан серверу при вызовах ajax. Интегрировать его в данные и модели ваших публикаций беспорядочно и ненужно. Добавление токена в качестве настраиваемого заголовка является чистым и многоразовым - и вы можете настроить его, чтобы вам не приходилось делать это каждый раз.

Есть исключение - ненавязчивый ajax не требует специальной обработки вызовов ajax. Токен передается как обычно в обычном скрытом поле ввода. Точно так же, как обычный POST.

_Layout.cshtml

В _layout.cshtml у меня есть этот блок JavaScript. Он не записывает токен в DOM, а использует jQuery для извлечения его из скрытого входного литерала, который генерирует помощник MVC. Строка Magic, которая является именем заголовка, определяется как константа в классе атрибута.

<script type="text/javascript">
    $(document).ready(function () {
        var isAbsoluteURI = new RegExp('^(?:[a-z]+:)?//', 'i');
        //http://stackoverflow.com/questions/10687099/how-to-test-if-a-url-string-is-absolute-or-relative

        $.ajaxSetup({
            beforeSend: function (xhr) {
                if (!isAbsoluteURI.test(this.url)) {
                    //only add header to relative URLs
                    xhr.setRequestHeader(
                       '@.ValidateAntiForgeryTokenOnAllPosts.HTTP_HEADER_NAME', 
                       $('@Html.AntiForgeryToken()').val()
                    );
                }
            }
        });
    });
</script>

Обратите внимание на использование одинарных кавычек в функции beforeSend - отображаемый элемент ввода использует двойные кавычки, которые нарушают литерал JavaScript.

Клиентский JavaScript

Когда это выполняется, вызывается функция beforeSend, указанная выше, и AntiForgeryToken автоматически добавляется в заголовки запроса.

$.ajax({
  type: "POST",
  url: "CSRFProtectedMethod",
  dataType: "json",
  contentType: "application/json; charset=utf-8",
  success: function (data) {
    //victory
  }
});

Серверная библиотека

Для обработки нестандартного токена требуется настраиваемый атрибут. Это основано на решении @viggity, но корректно обрабатывает ненавязчивый ajax. Этот код можно спрятать в вашей общей библиотеке

[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)]
public class ValidateAntiForgeryTokenOnAllPosts : AuthorizeAttribute
{
    public const string HTTP_HEADER_NAME = "x-RequestVerificationToken";

    public override void OnAuthorization(AuthorizationContext filterContext)
    {
        var request = filterContext.HttpContext.Request;

        //  Only validate POSTs
        if (request.HttpMethod == WebRequestMethods.Http.Post)
        {

            var headerTokenValue = request.Headers[HTTP_HEADER_NAME];

            // Ajax POSTs using jquery have a header set that defines the token.
            // However using unobtrusive ajax the token is still submitted normally in the form.
            // if the header is present then use it, else fall back to processing the form like normal
            if (headerTokenValue != null)
            {
                var antiForgeryCookie = request.Cookies[AntiForgeryConfig.CookieName];

                var cookieValue = antiForgeryCookie != null
                    ? antiForgeryCookie.Value
                    : null;

                AntiForgery.Validate(cookieValue, headerTokenValue);
            }
            else
            {
                new ValidateAntiForgeryTokenAttribute()
                    .OnAuthorization(filterContext);
            }
        }
    }
}

Сервер / Контроллер

Теперь вы просто применяете атрибут к своему действию. Еще лучше вы можете применить атрибут к своему контроллеру, и все запросы будут проверены.

[HttpPost]
[ValidateAntiForgeryTokenOnAllPosts]
public virtual ActionResult CSRFProtectedMethod()
{
  return Json(true, JsonRequestBehavior.DenyGet);
}
person Will D    schedule 16.12.2014
comment
Идеальное решение, гораздо более централизованное. Спасибо - person David Freire; 02.04.2015
comment
Не могли бы вы объяснить более подробно, почему вы хотите добавить заголовок только для относительных URL-адресов? Это пролетело над моей головой. Отличное решение! - person MattM; 01.09.2015
comment
relative гарантирует, что заголовок устанавливается только для запросов, возвращающихся на ваш собственный сервер, поскольку настройка ajax охватывает все запросы, сделанные с помощью jquery, мы не хотим, чтобы токен отправлялся на запросы jsonp или CORS. Это может быть верно и для абсолютных URL-адресов, но относительные гарантированно будут в том же домене. - person Will D; 02.09.2015
comment
@WillD Мне понравилось ваше решение, но я был вынужден немного его изменить. Поскольку вы выбираете $.ajaxSetup для определения общего beforesend обработчика событий, может случиться так, что вы его перезапишете. Я нашел другое решение, в котором вы можете добавить второй обработчик, который также будет вызываться. Прекрасно работает и не нарушает вашу реализацию. - person Viper; 03.12.2015
comment
Есть ли у кого-нибудь версия ASP.net 5 для проверки атрибута AntiForgery клиентом? Эта версия не компилируется в последнюю версию! - person Rob McCabe; 15.01.2016
comment
Я получаю ошибку в JS -. недействителен в начале блока кода. Действительны только идентификаторы, ключевые слова, комментарии (и {. - person Spencer Sullivan; 15.11.2019

Не используйте Html.AntiForgeryToken. Вместо этого используйте AntiForgery.GetTokens и AntiForgery.Validate из веб-API, как описано в Предотвращение атак с подделкой межсайтовых запросов (CSRF) в приложении ASP.NET MVC .

person Edward Brey    schedule 06.03.2013
comment
Для методов действий контроллера, которые связывают тип модели сервера с опубликованным AJAX JSON, для использования подходящего связывателя модели требуется тип содержимого как application / json. К сожалению, это исключает использование данных формы, требуемых атрибутом [ValidateAntiForgeryToken], поэтому ваш метод - единственный способ заставить его работать. Мой единственный вопрос: работает ли он по-прежнему в веб-ферме или в нескольких экземплярах веб-ролей Azure? Вы @ Эдвард или кто-нибудь еще знаете, что это проблема? - person Richard B; 16.05.2013
comment
@ Эдвард Брей Не могли бы вы подробнее рассказать, почему мы не должны его использовать? - person Odys; 12.02.2014
comment
@Odys: В Html.AntiForgeryToken нет ничего неправильного, но у него есть недостатки: требуется форма, требуется jQuery и предполагается, что недокументированные детали реализации Html.AntiForgeryToken. Тем не менее, это нормально во многих контекстах. Мое заявление «Не используйте Html.AntiForgeryToken», вероятно, выглядит слишком сильным. Я имею в виду, что он не предназначен для использования с веб-API, в отличие от более гибкого AntiForgery.GetTokens. - person Edward Brey; 12.02.2014
comment
Спасибо! Мне пришлось немного изменить его, чтобы он работал для контроллера MVC5, но это было решение - person jao; 25.08.2014
comment
Это, конечно, не требует формы. Вам просто нужно проанализировать DOM по имени. Используя jquery, я могу добавить его в свой объект данных через data {__RequestVerificationToken: $ (input [name = __ RequestVerificationToken]). Val ()} - person Anthony Mason; 06.05.2016
comment
Этот ответ не дает никаких оснований для смелого утверждения, и основная мысль относится к неработающей ссылке. Как оказалось, мне действительно нужно использовать методы AntiForgery, потому что я хочу поместить токен в заголовок там, где я думаю, он должен быть, но где ASP.NET не смотрит - при использовании его атрибута ValidateAntiForgeryToken - person PandaWood; 13.05.2019
comment
@PandaWood Я обновил ссылку на новое расположение документа. - person Edward Brey; 14.05.2019

Я думаю, все, что вам нужно сделать, это убедиться, что ввод «__RequestVerificationToken» включен в запрос POST. Другая половина информации (т. Е. Токен в cookie пользователя) уже отправляется автоматически с запросом AJAX POST.

E.g.,

$("a.markAsDone").click(function (event) {
    event.preventDefault();
    $.ajax({
        type: "post",
        dataType: "html",
        url: $(this).attr("rel"),
        data: { 
            "__RequestVerificationToken":
            $("input[name=__RequestVerificationToken]").val() 
        },
        success: function (response) {
            // ....
        }
    });
});
person jball    schedule 02.11.2010
comment
После многих часов экспериментов с публикацией jQuery AJAX из страницы MVC (Razor) это был самый простой ответ из всего, что сработало для меня. Просто включите свои собственные поля данных (или, как я полагаю, viewModel) после токена в качестве нового фрагмента данных (но в пределах исходного объекта данных). - person Ralph Bacon; 07.09.2013
comment
Как бы это реализовать, если бы функция AJAX находилась на странице .html, а не на странице Razor? - person Bob the Builder; 13.08.2014
comment
Если ваша html-страница не имеет предоставленного сервера AntiForgeryToken, все равно это спорный вопрос. Если это так (не знаю, как вы его получаете в этом случае, но предполагая, что это так), то вышеперечисленное будет работать нормально. Если вы пытаетесь создать простую веб-страницу, которая отправит запрос на сервер, ожидающий указанный токен, а сервер не сгенерировал указанную страницу, то вам не повезло. В этом суть AntiForgeryToken ... - person jball; 14.08.2014

Я как раз реализовывал эту актуальную проблему в своем текущем проекте. Я сделал это для всех ajax-POST, которым требовался аутентифицированный пользователь.

Во-первых, я решил подключить свои вызовы jquery ajax, чтобы не повторяться слишком часто. этот фрагмент javascript гарантирует, что все вызовы ajax (post) будут добавлять мой токен проверки запроса к запросу. Примечание: имя __RequestVerificationToken используется платформой .Net, поэтому я могу использовать стандартные функции Anti-CSRF, как показано ниже.

$(document).ready(function () {
    var securityToken = $('[name=__RequestVerificationToken]').val();
    $('body').bind('ajaxSend', function (elm, xhr, s) {
        if (s.type == 'POST' && typeof securityToken != 'undefined') {
            if (s.data.length > 0) {
                s.data += "&__RequestVerificationToken=" + encodeURIComponent(securityToken);
            }
            else {
                s.data = "__RequestVerificationToken=" + encodeURIComponent(securityToken);
            }
        }
    });
});

В ваших представлениях, где вам нужно, чтобы токен был доступен для вышеуказанного javascript, просто используйте общий HTML-Helper. Вы можете добавить этот код где угодно. Я поместил его в оператор if (Request.IsAuthenticated):

@Html.AntiForgeryToken() // you can provide a string as salt when needed which needs to match the one on the controller

В вашем контроллере просто используйте стандартный механизм ASP.Net MVC Anti-CSRF. Я так и сделал (хотя на самом деле использовал соль).

[HttpPost]
[Authorize]
[ValidateAntiForgeryToken]
public JsonResult SomeMethod(string param)
{
    // do something
    return Json(true);
}

С помощью Firebug или аналогичного инструмента вы можете легко увидеть, как к вашим запросам POST теперь добавлен параметр __RequestVerificationToken.

person 360Airwalk    schedule 04.04.2012

Вы также можете сделать это:

$("a.markAsDone").click(function (event) {
    event.preventDefault();

    $.ajax({
        type: "post",
        dataType: "html",
        url: $(this).attr("rel"),
        data: $('<form>@Html.AntiForgeryToken()</form>').serialize(),
        success: function (response) {
        // ....
        }
    });
});

Здесь используется Razor, но если вы используете синтаксис WebForms, вы также можете использовать теги <%= %>

person Leonardo Garcia Crespo    schedule 06.05.2011
comment
это безумно полезно, если вы используете mvc. - person mslugx; 02.03.2021

В дополнение к моему комментарию против ответа @JBall, который помог мне на этом пути, это последний ответ, который работает для меня. Я использую MVC и Razor, и я отправляю форму с помощью jQuery AJAX, поэтому я могу обновить частичное представление с некоторыми новыми результатами, и я не хотел выполнять полную обратную передачу (и мерцание страницы).

Добавьте @Html.AntiForgeryToken() внутрь формы как обычно.

Мой код кнопки отправки AJAX (т.е. событие onclick):

//User clicks the SUBMIT button
$("#btnSubmit").click(function (event) {

//prevent this button submitting the form as we will do that via AJAX
event.preventDefault();

//Validate the form first
if (!$('#searchForm').validate().form()) {
    alert("Please correct the errors");
    return false;
}

//Get the entire form's data - including the antiforgerytoken
var allFormData = $("#searchForm").serialize();

// The actual POST can now take place with a validated form
$.ajax({
    type: "POST",
    async: false,
    url: "/Home/SearchAjax",
    data: allFormData,
    dataType: "html",
    success: function (data) {
        $('#gridView').html(data);
        $('#TestGrid').jqGrid('setGridParam', { url: '@Url.Action("GetDetails", "Home", Model)', datatype: "json", page: 1 }).trigger('reloadGrid');
    }
});

Я оставил действие «Успех», поскольку оно показывает, как обновляется частичное представление, содержащее MvcJqGrid, и как оно обновляется (очень мощная сетка jqGrid, и это отличная оболочка MVC для нее).

Мой метод контроллера выглядит так:

    //Ajax SUBMIT method
    [ValidateAntiForgeryToken]
    public ActionResult SearchAjax(EstateOutlet_D model) 
    {
        return View("_Grid", model);
    }

Я должен признать, что не являюсь поклонником POST-публикации всех данных формы в качестве модели, но если вам нужно это сделать, то это один из способов, который работает. MVC просто делает привязку данных слишком простой, поэтому я думаю, вместо того, чтобы подставлять 16 отдельных значений (или слабо типизированную FormCollection), это нормально. Если вы знаете лучше, дайте мне знать, поскольку я хочу создать надежный код MVC C #.

person Ralph Bacon    schedule 07.09.2013

нашел эту очень умную идею от https://gist.github.com/scottrippey/3428114 для каждого $ .ajax вызывает его, изменяет запрос и добавляет токен.

// Setup CSRF safety for AJAX:
$.ajaxPrefilter(function(options, originalOptions, jqXHR) {
    if (options.type.toUpperCase() === "POST") {
        // We need to add the verificationToken to all POSTs
        var token = $("input[name^=__RequestVerificationToken]").first();
        if (!token.length) return;

        var tokenName = token.attr("name");

        // If the data is JSON, then we need to put the token in the QueryString:
        if (options.contentType.indexOf('application/json') === 0) {
            // Add the token to the URL, because we can't add it to the JSON data:
            options.url += ((options.url.indexOf("?") === -1) ? "?" : "&") + token.serialize();
        } else if (typeof options.data === 'string' && options.data.indexOf(tokenName) === -1) {
            // Append to the data string:
            options.data += (options.data ? "&" : "") + token.serialize();
        }
    }
});
person masterlopau    schedule 07.07.2015
comment
Я пробовал несколько других альтернатив выше, это то, что решило для меня. - person HostMyBus; 25.04.2019
comment
Однако мне пришлось добавить if (options.contentType != false && options.contentType.indexOf('application/json') === 0) {, чтобы перехватывать вызовы Ajax, в которых не указан тип содержимого. - person HostMyBus; 25.04.2019

1. Определите функцию для получения токена с сервера

@function
{

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

2. Получите токен и установите заголовок перед отправкой на сервер

var token = '@TokenHeaderValue()';    

       $http({
           method: "POST",
           url: './MainBackend/MessageDelete',
           data: dataSend,
           headers: {
               'RequestVerificationToken': token
           }
       }).success(function (data) {
           alert(data)
       });

3. Проверка на сервере HttpRequestBase для метода, который вы обрабатываете Post / get

        string cookieToken = "";
        string formToken = "";
        string[] tokens = Request.Headers["RequestVerificationToken"].Split(':');
            if (tokens.Length == 2)
            {
                cookieToken = tokens[0].Trim();
                formToken = tokens[1].Trim();
            }
        AntiForgery.Validate(cookieToken, formToken);
person Tonman Neverwalk alone    schedule 28.08.2013

сначала используйте @ Html.AntiForgeryToken () в html

 $.ajax({
        url: "@Url.Action("SomeMethod", "SomeController")",
        type: 'POST',
        data: JSON.stringify(jsonObject),
        contentType: 'application/json; charset=utf-8',
        dataType: 'json',
        async: false,
        beforeSend: function (request) {
            request.setRequestHeader("RequestVerificationToken", $("[name='__RequestVerificationToken']").val());
        },
        success: function (msg) {
            alert(msg);
        }
person Amir Reza    schedule 30.09.2018

Я знаю, что этот вопрос был опубликован некоторое время назад, но я нашел действительно полезный ресурс, в котором обсуждается использование AntiForgeryToken и упрощается его использование. Он также предоставляет плагин jquery для простого включения токена защиты от подделки в вызовы AJAX:

Рецепты запросов защиты от подделки для ASP.NET MVC и AJAX

Я мало что делаю, но, возможно, кому-то это пригодится.

person slawek    schedule 27.02.2012
comment
Этот пост похож на милю! Я уверен, что это здорово, но tl; dr - person BritishDeveloper; 27.04.2012
comment
Жаль, потому что он хорошо освещает тему. В нем не только рассказывается, как использовать эту функцию, но и объясняется, какие проблемы она решает, и дается контекст, позволяющий понять, как ее правильно использовать. Когда дело доходит до безопасности, я думаю, что важно глубокое понимание. - person slawek; 27.04.2012
comment
Если это важно, это должно быть написано таким образом, чтобы люди его читали;) - person BritishDeveloper; 30.04.2012

Вот самый простой способ, который я видел. Примечание. Убедитесь, что в вашем представлении есть «@ Html.AntiForgeryToken ()».

  $("a.markAsDone").click(function (event) {
        event.preventDefault();
        var sToken = document.getElementsByName("__RequestVerificationToken")[0].value;
        $.ajax({
            url: $(this).attr("rel"),
            type: "POST",
            contentType: "application/x-www-form-urlencoded",
            data: { '__RequestVerificationToken': sToken, 'id': parseInt($(this).attr("title")) }
        })
        .done(function (data) {
            //Process MVC Data here
        })
        .fail(function (jqXHR, textStatus, errorThrown) {
            //Process Failure here
        });
    });
person Dominic Sputo    schedule 03.10.2018

Небольшое улучшение решения 360Airwalk. Это включает токен AntiForgeryToken в функцию javascript, поэтому @ Html.AntiForgeryToken () больше не нужно включать в каждое представление.

$(document).ready(function () {
    var securityToken = $('@Html.AntiForgeryToken()').attr('value');
    $('body').bind('ajaxSend', function (elm, xhr, s) {
        if (s.type == 'POST' && typeof securityToken != 'undefined') {
            if (s.data.length > 0) {
                s.data += "&__RequestVerificationToken=" + encodeURIComponent(securityToken);
            }
            else {
                s.data = "__RequestVerificationToken=" + encodeURIComponent(securityToken);
            }
        }
    });
});
person Barry MSIH    schedule 03.10.2014

Я использую сообщение ajax для запуска метода удаления (происходит из временной шкалы visjs, но это не актуально). Вот что я сестренка:

Это мой Index.cshtml

@Scripts.Render("~/bundles/schedule")
@Styles.Render("~/bundles/visjs")
@Html.AntiForgeryToken()

<!-- div to attach schedule to -->
<div id='schedule'></div>

<!-- div to attach popups to -->
<div id='dialog-popup'></div>

Все, что я добавил здесь, это @Html.AntiForgeryToken(), чтобы токен появился на странице

Затем в моем сообщении о ajax я использовал:

$.ajax(
    {
        type: 'POST',
        url: '/ScheduleWorks/Delete/' + item.id,
        data: {
            '__RequestVerificationToken': 
            $("input[name='__RequestVerificationToken']").val()
              }
     }
);

Что добавляет значение токена, соскобленное со страницы, в опубликованные поля.

Перед этим я попытался поместить значение в заголовки, но получил ту же ошибку.

Не стесняйтесь публиковать улучшения. Это определенно кажется простым подходом, который я могу понять.

person Nick.McDermaid    schedule 26.06.2017

Хорошо, много сообщений здесь, ни один из них не помог мне, дни и дни google, и все равно я дошел до того, что написал все приложение с нуля, а затем я заметил этот маленький самородок в моем Web.confg

 <httpCookies requireSSL="false" domain="*.localLookup.net"/>

Теперь я не знаю, почему я добавил его, но с тех пор я заметил, что он игнорируется в режиме отладки, а не в производственном режиме (IE установлен в IIS где-то)

Для меня решение было одним из двух вариантов, так как я не помню, почему я его добавил, я не могу быть уверен, что другие вещи не зависят от него, а во-вторых, имя домена должно быть полностью в нижнем регистре, а TLD не так, как ive done в * .localLookup.net

Может, это помогает, а может и нет. Надеюсь, это кому-то поможет

person Agitated    schedule 29.11.2018

Решение, которое я нашел, не для ASPX, а для Razor, но вполне совместимая проблема.

Я решил это, добавив в запрос AntiForgery. Помощник HTML не создает идентификатор HTML с вызовом

@Html.AntiForgeryToken()

Чтобы добавить токен в постзапрос, я просто добавил идентификатор AntiForgery в скрытое поле с помощью jquery:

$("input[name*='__RequestVerificationToken']").attr('id', '__AjaxAntiForgeryForm');

Это заставило контроллер принять запрос с атрибутом [ValidateAntiForgeryToken].

person Dominik Sand    schedule 12.09.2019

AntiforgeryToken по-прежнему является проблемой, ни один из приведенных выше примеров не работал для меня дословно. Слишком много там. Я объединил их все. Нужен @ Html.AntiforgeryToken в форме, которая висит вокруг iirc

Решено так:

function Forgizzle(eggs) {
    eggs.__RequestVerificationToken =  $($("input[name=__RequestVerificationToken]")[0]).val();
    return eggs;
}

$.ajax({
            url: url,
            type: 'post',
            data: Forgizzle({ id: id, sweets: milkway }),
});

Если сомневаетесь, добавьте еще $ знаков

person Hazza    schedule 09.11.2011