jQuery Ajax calls and the Html.AntiForgeryToken()
Asked Answered
T

21

216

I have implemented in my app the mitigation to CSRF attacks following the informations that I have read on some blog post around the internet. In particular these post have been the driver of my implementation

Basically those articles and recommendations says that to prevent the CSRF attack anybody should implement the following code:

  1. Add the [ValidateAntiForgeryToken] on every action that accept the POST Http verb

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

  2. Add the <%= Html.AntiForgeryToken() %> helper inside forms that submits data to the server

Anyway in some parts of my app I am doing Ajax POSTs with jQuery to the server without having any form at all. This happens for example where I am letting the user to click on an image to do a specific action.

Suppose I have a table with a list of activities. I have an image on a column of the table that says "Mark activity as completed" and when the user click on that activity I am doing the Ajax POST as in the following sample:

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

How can I use the <%= Html.AntiForgeryToken() %> in these cases? Should I include the helper call inside the data parameter of the Ajax call?

Sorry for the long post and thanks very much for helping out

EDIT:

As per jayrdub answer I have used in the following way

$("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) {
            // ....
        }
    });
});
Theona answered 2/11, 2010 at 0:39 Comment(1)
The David Hayden link now 404s, it appears that he's migrated his blog to a new CMS, but didn't migrate all the old content over.Kaleidoscopic
T
261

I use a simple js function like this

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

Since every form on a page will have the same value for the token, just put something like this in your top-most master page

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

Then in your ajax call do (edited to match your second example)

$.ajax({
    type: "post",
    dataType: "html",
    url: $(this).attr("rel"),
    data: AddAntiForgeryToken({ id: parseInt($(this).attr("title")) }),
    success: function (response) {
        // ....
    }
});
Tonl answered 2/11, 2010 at 1:14 Comment(18)
Nice, I like the encapsulation of the token fetching.Pyrrolidine
Yeah, that's nice since a lot of times you do ajax posts you don't even have a form on the page to get the token fromTonl
@jayrdub: thanks for your response! Where do I have to place the small js function? I have placed inside a js file of common function and either in the master page but Chrome give me an error when I use it. It says Unexpected token (. Please see my question edit to see how I am using it.Theona
@Lorenzo, put your custom data inside the call to AddAntiForgeryToken, like so: data: AddAntiForgeryToken({ id: parseInt($(this).attr("title")) }),Pyrrolidine
jay, nice one. useful generic implementation. been working on the edges of something (less nice) for quite some time.Adhern
@Tonl What's the purpose of passing the id to the AddAntiForgeryToken()Cobol
That's just an example of how to merge the antiforgery token into whatever data object you want to send. Nothing special about it, it just matches the OP's example.Tonl
Re the above comment, when I implemented it, my url already had parameters in it (e.g. /action/id=444), so I simply didn't pass anything to AddAntiForgeryToken({}) , and it worked fine for me.Retouch
How bad an idea would it be to use ajaxSend or override ajax to always augment data with the anti-forgery token? Maybe adding some check to make sure that the url is destined for your server.Runagate
Be careful if you use output cache.Jordaens
what happens when i make a second ajax request on the same page without reloading? wouldn't the token be invalid then?Lucialucian
@SouhaiebBesbes the validation token should be the same for a user across all pages (it works in conjunction with a cookie that is set and stays the same). So it doesn't matter if there are multiple requests per page, it'll be the same if the base page reloaded anyway.Tonl
@Tonl thanks, it turns i misunderstood how those tokens workLucialucian
this will not work with outputcache enabled on your pageGadgeteer
@Gadgeteer true, depending on the outputcache configuration. Caching the same output for different users is definitely a common usage, but it is possible to have the outputcache keyed on the same user.Tonl
@Tonl Is the form required or can you just do: @Html.AntiForgeryToken() on the page? In my case my page only lists a bunch of products in a grid, but I am doing the deleting with an AJAX request so need the token, but don't want to add an empty form to my page if not necessary.Chrystal
@BlakeRivell I doubt it is required for it to work, the form is there to keep the html valid.Tonl
Why isn't it added to the header instead? Is there a preferable way?Akan
J
33

I like the solution provided by 360Airwalk, but it may be improved a bit.

The first problem is that if you make $.post() with empty data, jQuery doesn't add a Content-Type header, and in this case ASP.NET MVC fails to receive and check the token. So you have to ensure the header is always there.

Another improvement is support of all HTTP verbs with content: POST, PUT, DELETE etc. Though you may use only POSTs in your application, it's better to have a generic solution and verify that all data you receive with any verb has an anti-forgery token.

$(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);
            }
        }
    });
});
Joannajoanne answered 24/8, 2012 at 20:41 Comment(11)
+1 you are right, i've not thought of the empty post call issue. thanks for the input. you were right about that we do not use delete/put yet in our project.Micheal
+1 for saving me from having to add the function to all the jQuery.Ajax callsDupree
+1 Just as a note for posterity, the jQuery documentation for .ajaxSend() states "As of jQuery 1.8, the .ajaxSend() method should only be attached to document." api.jquery.com/ajaxsendStaple
@Joannajoanne Where does the options come from, which is listed in the final if statement? Thanks.Cornu
Beware using this if you have multiple forms on a page. You will need to set the value in beforeSend with a more specific selector call instead of for the document.Catnap
I'm unsure why this is so complex, why can it not just set the token and be done with 1 line of code as done here (if we remove the if condition for "POST") - erlend.oftedal.no/blog/static-118.html?blogid=118Photosensitive
@Photosensitive My main points were that 1) it is not enough to cover POST requests only, you should do the same for all requests having any content, and 2) if you resort to the hidden field method, you must include a Content-Type header.Joannajoanne
@PandaWood, my answer was a modification of another answer which used a hidden form field to pass the token, hence the complexities. This method does not require changes on the server because ASP.NET MVC supports the hidden form field out-of-the-box. One can also use a header like X-CSRF-Token, but it will require a token validation code on a server as described in docs.microsoft.com/en-us/aspnet/web-api/overview/security/…. So it is up to you to decide. There is already another answer in the topic suggesting to use a header instead.Joannajoanne
Thanks @Joannajoanne yes I failed to understand that ASP.NET doesn't read from the header (by default) and so this is necessary to cater for what the ValidateAntiForgeryTokenAttribute doesPhotosensitive
@Joannajoanne what about forms with formData and processData/contentType set to false?Laxative
Never mind, the issue was appending the actual token. I used if (opt.data instanceof FormData) { opt.data.append("__RequestVerificationToken", securityToken); } else...Laxative
B
25

I know there are a lot of other answers, but this article is nice and concise and forces you to check all of your HttpPosts, not just some of them:

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

It uses HTTP headers instead of trying to modify the form collection.

Server

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

Client

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 () {
        ...
    }
});
Boman answered 17/1, 2014 at 21:15 Comment(5)
The attribute from the article you linked too combined with Bronx's response is the ultimate DRY solution to this problem.Clinker
Great find. I edited your answer to include the code snippets so the answer stands on its own, but I hope people will read the rest of the article as well. This appears to be a very clean solution.Irving
thanks Tim, it is an excellent idea, its frustrating when a link goes dead and the answer becomes worthless. I've started doing this on all my new answers.Boman
Is this MVC, WebAPI or .NetCore ? I can't get the correct namespaces for WebAPI 5Tendinous
It's also worth checking PATCH and PUT verbs. If you're using those.Hickory
R
23

I feel like an advanced necromancer here, but this is still an issue 4 years later in MVC5.

To handle ajax requests properly the anti-forgery token needs to be passed to the server on ajax calls. Integrating it into your post data and models is messy and unnecessary. Adding the token as a custom header is clean and reusable - and you can configure it so you don't have to remember to do it every time.

There is an exception - Unobtrusive ajax does not need special treatment for ajax calls. The token is passed as usual in the regular hidden input field. Exactly the same as a regular POST.

_Layout.cshtml

In _layout.cshtml I have this JavaScript block. It doesn't write the token into the DOM, rather it uses jQuery to extract it from the hidden input literal that the MVC Helper generates. The Magic string that is the header name is defined as a constant in the attribute class.

<script type="text/javascript">
    $(document).ready(function () {
        var isAbsoluteURI = new RegExp('^(?:[a-z]+:)?//', 'i');
        //https://mcmap.net/q/128414/-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>

Note the use of single quotes in the beforeSend function - the input element that is rendered uses double quotes that would break the JavaScript literal.

Client JavaScript

When this executes the beforeSend function above is called and the AntiForgeryToken is automatically added to the request headers.

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

Server Library

A custom attribute is required to process the non standard token. This builds on @viggity's solution, but handles unobtrusive ajax correctly. This code can be tucked away in your common library

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

Server / Controller

Now you just apply the attribute to your Action. Even better you can apply the attribute to your controller and all requests will be validated.

[HttpPost]
[ValidateAntiForgeryTokenOnAllPosts]
public virtual ActionResult CSRFProtectedMethod()
{
  return Json(true, JsonRequestBehavior.DenyGet);
}
Roush answered 16/12, 2014 at 2:57 Comment(5)
Can you explain in more detail why you only want to add the header for relative URLs? That went over my head. Great solution!Lontson
relative ensures that the header is only set on requests going back to your own server, as the ajax setup covers all requests made with jquery, we don't want the token being sent on jsonp or CORS requets. This maybe true for absolute urls too, but relative are guaranteed to be same domain.Roush
@WillD I liked your solution, but was forced to modify it a bit. Because you choose $.ajaxSetup to define a general beforesend eventhandler it can happen that you overwrite it. I found another solution where you can add a second handler which will also be called. Works nicely and doesn't break your implementation.Baryram
Does anyone have an ASP.net 5 version of the customer validate AntiForgery attribute? This version does not compile in the latest version!Crouse
I get an error in the JS - "." is not valid at the start of a code block. Only identifiers, keywords, comments, "(" and "{" are valid.Bucharest
T
20

Don't use Html.AntiForgeryToken. Instead, use AntiForgery.GetTokens and AntiForgery.Validate from Web API as described in Preventing Cross-Site Request Forgery (CSRF) Attacks in ASP.NET MVC Application.

Terchie answered 6/3, 2013 at 18:33 Comment(7)
For controller action methods that model bind a server model type to the posted AJAX JSON, having the content type as "application/json" is required for the proper model binder to be used. Unfortunately, this precludes using form data, required by the [ValidateAntiForgeryToken] attribute, so your method is the only way I could find to make it work. My only question is, does it still work in a web farm or multiple Azure web role instances? Do you @Edward, or anyone else know if this is a problem?Blake
@Edward Brey Can you please elaborate on why we shouldn't use it?Grimes
@Odys: There's nothing inherently wrong with Html.AntiForgeryToken, but it has downsides: requires a form, requires jQuery, and assumes undocumented Html.AntiForgeryToken implementation details. Still, it's fine in many contexts. My statement "Don't use Html.AntiForgeryToken" probably comes off too strong. My meaning is that it's not intended to be used with Web API, whereas the more flexible AntiForgery.GetTokens is.Terchie
thx! I had to change it a bit to make it work for an MVC5 controller, but this was the solutionOdy
It certainly does not require a form. You just need to parse the DOM for it by name. Using jquery, I can add it inside my data object via data { __RequestVerificationToken: $("input[name=__RequestVerificationToken]").val() }Gaylor
This answer provides no reasoning for a bold assertion and the main point refers to a dead link. As it turns out, I do need to use AntiForgery methods, because I want to put the token into the header where I think it should be, but where ASP.NET doesn't look - when using its ValidateAntiForgeryToken attributePhotosensitive
@Photosensitive I updated the link to the new doc location.Terchie
P
19

I think all you have to do is ensure that the "__RequestVerificationToken" input is included in the POST request. The other half of the information (i.e. the token in the user's cookie) is already sent automatically with an AJAX POST request.

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) {
            // ....
        }
    });
});
Pyrrolidine answered 2/11, 2010 at 1:3 Comment(3)
After many hours experimenting with jQuery AJAX posting from within an MVC (Razor) page this was the simplest answer of all that worked for me. Just include your own data fields (or the viewModel I suppose) after the token as a new piece of data (but within the original data object).Rodl
How would I implement this if the AJAX function was in a .html page and not a Razor page?Squinty
If your html page doesn't have a server supplied AntiForgeryToken it's all moot anyhow. If it does (not sure how you're getting one in that case, but assuming you are), then the above would work just fine. If you're attempting to create a simply webpage that will post a request to a server expecting said token, and the server did not generate said page, then you're out of luck. That's essentially the point of the AntiForgeryToken...Pyrrolidine
M
16

I was just implementing this actual problem in my current project. i did it for all ajax-POSTs that needed an authenticated user.

First off i decided to hook my jquery ajax calls so i do not to repeat myself too often. this javascript snippet ensures all ajax (post) calls will add my request validation token to the request. Note: the name __RequestVerificationToken is used by the .Net framework so i can utilize the standard Anti-CSRF features as shown below.

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

In your Views where you need the token to be available to the above javascript just use the common HTML-Helper. You can basically add this code whereever you want. I placed it within a if(Request.IsAuthenticated) statement:

@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 mechanism. I did it like this (though i actually used 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 have a __RequestVerificationToken parameter appended.

Micheal answered 4/4, 2012 at 15:44 Comment(0)
C
8

You can do this also:

$("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) {
        // ....
        }
    });
});

This is using Razor, but if you're using WebForms syntax you can just as well use <%= %> tags

Caslon answered 6/5, 2011 at 18:7 Comment(1)
this is crazy helpful if you are using mvc.Buxom
R
4

Further to my comment against @JBall's answer that helped me along the way, this is the final answer that works for me. I'm using MVC and Razor and I'm submitting a form using jQuery AJAX so I can update a partial view with some new results and I didn't want to do a complete postback (and page flicker).

Add the @Html.AntiForgeryToken() inside the form as usual.

My AJAX submission button code (i.e. an onclick event) is:

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

I've left the "success" action in as it shows how the partial view is being updated that contains an MvcJqGrid and how it's being refreshed (very powerful jqGrid grid and this is a brilliant MVC wrapper for it).

My controller method looks like this:

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

I have to admit to not being a fan of POSTing an entire form's data as a Model but if you need to do it then this is one way that works. MVC just makes the data binding too easy so rather than subitting 16 individual values (or a weakly-typed FormCollection) this is OK, I guess. If you know better please let me know as I want to produce robust MVC C# code.

Rodl answered 7/9, 2013 at 14:12 Comment(0)
M
4

found this very clever idea from https://gist.github.com/scottrippey/3428114 for every $.ajax calls it modifies the request and add the token.

// 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();
        }
    }
});
Mickens answered 7/7, 2015 at 22:53 Comment(2)
I tried several of the other alternatives above, this is what solved it for me.Distrustful
I did however have to add if (options.contentType != false && options.contentType.indexOf('application/json') === 0) { to catch Ajax calls that had not specified a content typeDistrustful
H
3

1.Define Function to get Token from server

@function
{

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

2.Get token and set header before send to server

var token = '@TokenHeaderValue()';    

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

3. Onserver Validation on HttpRequestBase on method you handle 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);
Hanni answered 28/8, 2013 at 20:24 Comment(0)
W
3

first use @Html.AntiForgeryToken() in 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);
        }
Wayfaring answered 30/9, 2018 at 7:32 Comment(0)
N
1

I aware it's been some time since this question was posted, but I found really useful resource, which discusses usage of AntiForgeryToken and makes it less troublesome to use. It also provides jquery plugin for easily including antiforgery token in AJAX calls:

Anti-Forgery Request Recipes For ASP.NET MVC And AJAX

I'm not contributing much, but maybe someone will find it useful.

Namedropping answered 27/2, 2012 at 15:10 Comment(3)
That post is like a mile long! I'm sure it's great but tl;drSora
Too bad, because it nicely covers subject. It not only tells you how to use the feature, but explains what problem it fixes and give you context to understand how to use it correctly. When it comes to security I think in-depth understanding is important.Namedropping
If it's important it should be written in a way that encourages people to read it ;)Sora
H
1

Here is the easiest way I've seen. Note: Make sure you have "@Html.AntiForgeryToken()" in your View

  $("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
        });
    });
Horror answered 3/10, 2018 at 19:54 Comment(0)
H
0

Slight improvement to 360Airwalk solution. This imbeds the Anti Forgery Token within the javascript function, so @Html.AntiForgeryToken() no longer needs to be included on every view.

$(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);
            }
        }
    });
});
Henceforward answered 3/10, 2014 at 18:56 Comment(0)
L
0
function DeletePersonel(id) {

    var data = new FormData();
    data.append("__RequestVerificationToken", "@HtmlHelper.GetAntiForgeryToken()");

    $.ajax({
        type: 'POST',
        url: '/Personel/Delete/' + id,
        data: data,
        cache: false,
        processData: false,
        contentType: false,
        success: function (result) {
        }
    });
}

public static class HtmlHelper {
    public static string GetAntiForgeryToken() {
        System.Text.RegularExpressions.Match value = 
                System.Text.RegularExpressions.Regex.Match(System.Web.Helpers.AntiForgery.GetHtml().ToString(), 
                        "(?:value=\")(.*)(?:\")");
        if (value.Success) {
            return value.Groups[1].Value;
        }
        return "";
    }
}
Liv answered 1/9, 2016 at 18:45 Comment(0)
D
0

I'm using a ajax post to run a delete method (happens to be from a visjs timeline but that's not relelvant). This is what I sis:

This is my 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>

All I added here was @Html.AntiForgeryToken() to make the token appear in the page

Then in my ajax post I used:

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

Which adds the token value, scraped off the page, to the fields posted

Before this I tried putting the value in the headers but I got the same error

Feel free to post improvements. This certainly seems to be a simple approach that I can understand

Davina answered 26/6, 2017 at 10:19 Comment(0)
B
0

Okay lots of posts here, none of them helped me, days and days of google, and still no further I got to the point the wr-writing the whole app from scratch, and then I noticed this little nugget in my Web.confg

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

Now I don't know why I added it however I have since noticed, its ignored in debug mode and not in a production mode (IE Installed to IIS Somewhere)

For me the solution was one of 2 options, since I don't remember why I added it I cant be sure other things don't depend on it, and second the domain name must be all lower case and a TLD not like ive done in *.localLookup.net

Maybe it helps maybe it don't. I hope it does help someone

Bellabelladonna answered 29/11, 2018 at 1:36 Comment(0)
A
0

The Solution i found is not for ASPX but for Razor, but quite comperable issue.

I resolved it by adding the AntiForgery to the request. The HTML Helper does not create a HTML id with the call

@Html.AntiForgeryToken()

In order to add the token to the postrequest i just added the AntiForgery id to the hidden field with jquery:

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

This caused the controller to accept the request with the [ValidateAntiForgeryToken] attribute

Apportion answered 12/9, 2019 at 13:59 Comment(0)
C
0

Most of the answers above are for MVC applications. Yes, you need to add @Html.AntiForgeryToken() on your cshtml file and also get the security token in the js file and use it on you ajax post's data object.

However on the c# side, just a reminder, if you are using Razor pages, note that you cannot use [ValidateAntiForgeryToken] attribute on top of the methods. It will not give you an error but it will not have any effect since it only works on MVC apps. For razor pages, you need to use the attribute on top of the "model" class.

For example;

[ValidateAntiForgeryToken]
public class IndexModel : PageModel
{
     // rest of the class
}

This will apply the attribute to all the methods on the page.

Cartel answered 29/4, 2023 at 17:48 Comment(0)
N
-4

AntiforgeryToken is still a pain, none of the examples above worked word for word for me. Too many for's there. So I combined them all. Need a @Html.AntiforgeryToken in a form hanging around iirc

Solved as so:

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

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

When in doubt, add more $ signs

Naval answered 9/11, 2011 at 12:45 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.