How can I provide AntiForgeryToken when publishing JSON data using $ .ajax?

I am using the following code for this post:

First, I will populate an array variable with the correct values ​​for the controller action.

Using the code below, I think it should be very simple by simply adding the following line to your JavaScript code:

data["__RequestVerificationToken"] = $('[name=__RequestVerificationToken]').val(); 

<%= Html.AntiForgeryToken() %> is in the right place, and the action has [ValidateAntiForgeryToken]

But my controller action continues to say: "Invalid fake token"

What am I doing wrong here?

The code

 data["fiscalyear"] = fiscalyear; data["subgeography"] = $(list).parent().find('input[name=subGeography]').val(); data["territories"] = new Array(); $(items).each(function() { data["territories"].push($(this).find('input[name=territory]').val()); }); if (url != null) { $.ajax( { dataType: 'JSON', contentType: 'application/json; charset=utf-8', url: url, type: 'POST', context: document.body, data: JSON.stringify(data), success: function() { refresh(); } }); } 
+60
json ajax asp.net-mvc antiforgerytoken
May 25 '10 at 17:04
source share
12 answers

You do not need a ValidationHttpRequestWrapper solution with MVC 4. According to this link .

  • Put the token in the headers.
  • Create a filter.
  • Put the attribute in your method.

Here is my solution:

 var token = $('input[name="__RequestVerificationToken"]').val(); var headers = {}; headers['__RequestVerificationToken'] = token; $.ajax({ type: 'POST', url: '/MyTestMethod', contentType: 'application/json; charset=utf-8', headers: headers, data: JSON.stringify({ Test: 'test' }), dataType: "json", success: function () {}, error: function (xhr) {} }); [AttributeUsage(AttributeTargets.Method | AttributeTargets.Class, AllowMultiple = false, Inherited = true)] public class ValidateJsonAntiForgeryTokenAttribute : FilterAttribute, IAuthorizationFilter { public void OnAuthorization(AuthorizationContext filterContext) { if (filterContext == null) { throw new ArgumentNullException("filterContext"); } var httpContext = filterContext.HttpContext; var cookie = httpContext.Request.Cookies[AntiForgeryConfig.CookieName]; AntiForgery.Validate(cookie != null ? cookie.Value : null, httpContext.Request.Headers["__RequestVerificationToken"]); } } [HttpPost] [AllowAnonymous] [ValidateJsonAntiForgeryToken] public async Task<JsonResult> MyTestMethod(string Test) { return Json(true); } 
+45
Jun 24 '14 at 19:19
source share

What is wrong is that the controller action that should process this request and which is marked with the [ValidateAntiForgeryToken] sign expects a parameter named __RequestVerificationToken be sent along with the request.

There is no such POSTed parameter when you use JSON.stringify(data) , which converts your form into its JSON representation and therefore an exception is thrown.

So, I see two possible solutions:

Number 1: use x-www-form-urlencoded instead of JSON to send your request parameters:

 data["__RequestVerificationToken"] = $('[name=__RequestVerificationToken]').val(); data["fiscalyear"] = fiscalyear; // ... other data if necessary $.ajax({ url: url, type: 'POST', context: document.body, data: data, success: function() { refresh(); } }); 

Number 2: Separate the query into two parameters:

 data["fiscalyear"] = fiscalyear; // ... other data if necessary var token = $('[name=__RequestVerificationToken]').val(); $.ajax({ url: url, type: 'POST', context: document.body, data: { __RequestVerificationToken: token, jsonRequest: JSON.stringify(data) }, success: function() { refresh(); } }); 

Thus, in all cases, you need to send the __RequestVerificationToken value.

+44
May 25 '10 at 17:10
source share

I just implemented this actual problem in my current project. I did this for all AJAX POSTs that need an authenticated user.

Firstly, I decided to hook up my jQuery Ajax calls, so I don't repeat too often. This piece of JavaScript code ensures that all ajax (post) calls add the request validation token to the request. Note: the name __RequestVerificationToken is used by the .NET platform, so I can use the standard Anti-CSRF functions, as shown below.

 $(document).ready(function () { 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); } } }); }); 

In your views, where you need a token available for the above JavaScript code, just use the generic HTML helper. You can basically add this code wherever you want. I put it in the if statement (Request.IsAuthenticated):

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

In your controller, simply use the standard ASP.NET MVC anti-CSRF engine. I did it like this (although I did use salt).

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

With Firebug or a similar tool, you can easily see how your POST requests now contain the __RequestVerificationToken parameter.

+11
Apr 04 2018-12-12T00:
source share

You can set the attribute $. ajax traditional and set it to true to send the json data as a URL encoded form. Be sure to set type:'POST' . With this method, you can even send arrays, and you do not need to use JSON.stringyfy or any changes on the server side (for example, creating custom attributes for the sniff header)

I tried this in ASP.NET MVC3 and jquery 1.7 settings and worked

Below is a snippet of code.

 var data = { items: [1, 2, 3], someflag: true}; data.__RequestVerificationToken = $(':input[name="__RequestVerificationToken"]').val(); $.ajax({ url: 'Test/FakeAction' type: 'POST', data: data dataType: 'json', traditional: true, success: function (data, status, jqxhr) { // some code after succes }, error: function () { // alert the error } }); 

This will be consistent with the MVC action with the following signature

 [HttpPost] [Authorize] [ValidateAntiForgeryToken] public ActionResult FakeAction(int[] items, bool someflag) { } 
+6
11 Oct '13 at 19:03
source share

I hold a marker in my JSON object and I have finished modifying the ValidateAntiForgeryToken class to check the InputStream Request object when the message is json. I wrote a blog post about this, hope you may find it helpful.

+5
Sep 24 '10 at 14:41
source share

You will never have to check AntiForgeryToken when you receive a JSON message.

The reason is that AntiForgeryToken was created to prevent CSRF. Since you cannot send AJAX data to another host, and HTML forms cannot send JSON as the request body, you do not need to protect your application from published JSON.

+5
Sep 29 '11 at 20:43
source share

You cannot check content like contentType: 'application / json; charset = utf-8 ', because your date will not be loaded in the request form property, but in the InputStream property, and you will never have this Request.Form ["__ RequestVerificationToken"].

It will always be empty, and verification will not complete.

+4
Jul 12 '11 at 13:37
source share

Check out the Dixin Blog for a great post on how to do this.

Also, why not use $ .post instead of $ .ajax?

Along with the jQuery plugin on this page, you can do something simple:

  data = $.appendAntiForgeryToken(data,null); $.post(url, data, function() { refresh(); }, "json"); 
+2
Jul 09 '10 at 4:41
source share

I had to be a bit shadowy to check anti-fake tokens when posting JSON, but it worked.

 //If it not a GET, and the data they're sending is a string (since we already had a separate solution in place for form-encoded data), then add the verification token to the URL, if it not already there. $.ajaxSetup({ beforeSend: function (xhr, options) { if (options.type && options.type.toLowerCase() !== 'get' && typeof (options.data) === 'string' && options.url.indexOf("?__RequestVerificationToken=") < 0 && options.url.indexOf("&__RequestVerificationToken=") < 0) { if (options.url.indexOf('?') < 0) { options.url += '?'; } else { options.url += '&'; } options.url += "__RequestVerificationToken=" + encodeURIComponent($('input[name=__RequestVerificationToken]').val()); } } }); 

But, as several people have already mentioned, validation only checks the form - not JSON, not the query string. So, we redefined the attribute behavior. Re-implementing the entire validation would be horrible (and probably unsafe), so I just overridden the Form property, if the token was passed to the QueryString, it has a built-in validation, THINK, which was on the form.

This is a bit complicated because the form is read-only, but doable.

  if (IsAuth(HttpContext.Current) && !IsGet(HttpContext.Current)) { //if the token is in the params but not the form, we sneak in our own HttpContext/HttpRequest if (HttpContext.Current.Request.Params != null && HttpContext.Current.Request.Form != null && HttpContext.Current.Request.Params["__RequestVerificationToken"] != null && HttpContext.Current.Request.Form["__RequestVerificationToken"] == null) { AntiForgery.Validate(new ValidationHttpContextWrapper(HttpContext.Current), null); } else { AntiForgery.Validate(new HttpContextWrapper(HttpContext.Current), null); } } //don't validate un-authenticated requests; anyone could do it, anyway private static bool IsAuth(HttpContext context) { return context.User != null && context.User.Identity != null && !string.IsNullOrEmpty(context.User.Identity.Name); } //only validate posts because that what CSRF is for private static bool IsGet(HttpContext context) { return context.Request.HttpMethod.ToUpper() == "GET"; } 

...

 internal class ValidationHttpContextWrapper : HttpContextBase { private HttpContext _context; private ValidationHttpRequestWrapper _request; public ValidationHttpContextWrapper(HttpContext context) : base() { _context = context; _request = new ValidationHttpRequestWrapper(context.Request); } public override HttpRequestBase Request { get { return _request; } } public override IPrincipal User { get { return _context.User; } set { _context.User = value; } } } internal class ValidationHttpRequestWrapper : HttpRequestBase { private HttpRequest _request; private System.Collections.Specialized.NameValueCollection _form; public ValidationHttpRequestWrapper(HttpRequest request) : base() { _request = request; _form = new System.Collections.Specialized.NameValueCollection(request.Form); _form.Add("__RequestVerificationToken", request.Params["__RequestVerificationToken"]); } public override System.Collections.Specialized.NameValueCollection Form { get { return _form; } } public override string ApplicationPath { get { return _request.ApplicationPath; } } public override HttpCookieCollection Cookies { get { return _request.Cookies; } } } 

There are other things that differ from our solution (in particular, we use the HttpModule, so we do not need to add an attribute to each POST), which I forgot in favor of brevity. I can add it if necessary.

+1
Jul 15 '13 at 23:09
source share

I resolved it globally using RequestHeader.

 $.ajaxPrefilter(function (options, originalOptions, jqXhr) { if (options.type.toUpperCase() === "POST") { // We need to add the verificationToken to all POSTs if (requestVerificationTokenVariable.length > 0) jqXhr.setRequestHeader("__RequestVerificationToken", requestVerificationTokenVariable); } }); 

where requestVerificationTokenVariable is a variable string containing the value of the token. Then the whole ajax call sends the token to the server, but the ValidateAntiForgeryTokenAttribute defaults to Request.Form. I typed and added this globalFilter, which copies the token from the header into request.form, than I can use the default ValidateAntiForgeryTokenAttribute value:

 public static void RegisterGlobalFilters(GlobalFilterCollection filters) { filters.Add(new GlobalAntiForgeryTokenAttribute(false)); } public class GlobalAntiForgeryTokenAttribute : FilterAttribute, IAuthorizationFilter { private readonly bool autoValidateAllPost; public GlobalAntiForgeryTokenAttribute(bool autoValidateAllPost) { this.autoValidateAllPost = autoValidateAllPost; } private const string RequestVerificationTokenKey = "__RequestVerificationToken"; public void OnAuthorization(AuthorizationContext filterContext) { var req = filterContext.HttpContext.Request; if (req.HttpMethod.ToUpperInvariant() == "POST") { //gestione per ValidateAntiForgeryToken che gestisce solo il recupero da Request.Form (non disponibile per le chiamate ajax json) if (req.Form[RequestVerificationTokenKey] == null && req.IsAjaxRequest()) { var token = req.Headers[RequestVerificationTokenKey]; if (!string.IsNullOrEmpty(token)) { req.Form.SetReadOnly(false); req.Form[RequestVerificationTokenKey] = token; req.Form.SetReadOnly(true); } } if (autoValidateAllPost) AntiForgery.Validate(); } } } public static class NameValueCollectionExtensions { private static readonly PropertyInfo NameObjectCollectionBaseIsReadOnly = typeof(NameObjectCollectionBase).GetProperty("IsReadOnly", BindingFlags.FlattenHierarchy | BindingFlags.NonPublic | BindingFlags.Instance); public static void SetReadOnly(this NameValueCollection source, bool readOnly) { NameObjectCollectionBaseIsReadOnly.SetValue(source, readOnly); } } 

This work is for me :)

+1
Mar 22 '17 at 17:44
source share

Unfortunately, for me, the other answers are based on some query formatting processed by jquery, and none of them worked when setting up the payload directly. (To be fair, putting it in the headline would work, but I didn't want to go that route.)

To accomplish this in the beforeSend function, the following will be executed. $.params() converts the object to a standard format / url-encoded format.

I tried all sorts of options for substituting json with a token, and none of them worked.

 $.ajax({ ...other params..., beforeSend: function(jqXHR, settings){ var token = ''; //get token data = { '__RequestVerificationToken' : token, 'otherData': 'value' }; settings.data = $.param(data); } }); 

`` ``

0
Nov 19 '15 at 5:16
source share

You should put the AntiForgeryToken in the form tag:

 @using (Html.BeginForm(actionName:"", controllerName:"",routeValues:null, method: FormMethod.Get, htmlAttributes: new { @class="form-validator" })) { @Html.AntiForgeryToken(); } 

then in javascript change the following code

 var DataToSend = []; DataToSend.push(JSON.stringify(data),$('form.form-validator').serialize()); $.ajax( { dataType: 'JSON', contentType: 'application/json; charset=utf-8', url: url, type: 'POST', context: document.body, data: DataToSend, success: function() { refresh(); } }); 

then you can check the request using ActionResult annotations

 [ValidateAntiForgeryToken] [HttpPost] 

Hope this helps.

-one
Feb 20 '14 at 14:15
source share



All Articles