ASP.net MVC AntiForgeryToken over AJAX

I am currently developing an MVC application in ASP.net. I am using AJAX.ActionLink to provide a delete link in a list of records, however this is very insecure. I have put this:

<AcceptVerbs(HttpVerbs.Post)>

Over the function to do the deleting, which stops the function being called simply by a URL. However, the other security hole that still exists is that if i were to make a basic html page with this content:
<form action="http://foo.com/user/delete/260" method="post">
<input type="submit" />
</form>

It would still be perfoming a post, but from a different location.

Is it possible to use the AntiForgeryToken with an AJAX ActionLink? If so, is this a secure approach? Are there more security holes i haven’t realised?

Answers:

Thank you for visiting the Q&A section on Magenaut. Please note that all the answers may not help you solve the issue immediately. So please treat them as advisements. If you found the post helpful (or not), leave a comment & I’ll get back to you as soon as possible.

Method 1

Have a look at this blog post.

Say you have an Action method like
this:

[AcceptVerbs(HttpVerbs.Post),
ValidateAntiForgeryToken]
public ActionResult DeleteAccount(int
accountId) {
// delete stuff }

And you call it via:

$.post('/home/DeleteAccount', {
accountId: 1000 }, function() {
alert('Account Deleted.'); });

Since the POST does not include the
AntiForgeryToken, it will fail.

Fortunately, it doesn’t take much
brainpower to fix this. All the client
side component of AntiForgeryToken
does is put the token in a basic
hidden field. So, you just need to
pull that data out and include it in
your AJAX call.

var token =
$('input[name=__RequestVerificationToken]').val();

$.post('/home/DeleteAccount', {
accountId: 1000,
'__RequestVerificationToken': token },
function() {
alert('Account Deleted.'); });

Do note that if you have multiple
forms on the page with multiple
AntiForgeryTokens, you will have to
specify which one you want in your
jQuery selector. Another gotcha is if
you are using jQuery’s
serializeArray() function, you’ll have
to add it a bit differently:

var formData =
$('#myForm').serializeArray(); var
token =
$('input[name=__RequestVerificationToken]').val();
formData.push({ name:
'__RequestVerificationToken', value:
token });

$.post('/home/DeleteAccount',
formData, function() {
alert('Account Deleted.'); });

Update: The link has been fixed.

Method 2

You can use AntiForgeryToken with Ajax.ActionLink but you need to manually insert the AntiForgeryToken into the header of your request like so:

function GetAntiForgeryToken(){
   var tokenWindow = window;
   var tokenName = "__RequestVerificationToken";
   var tokenField = $(tokenWindow.document).find("input[type='hidden'][name='" +     tokenName +   "']");
   if (tokenField.length == 0) {return null;}
   else {
      return {
         name: tokenName,
         value: tokenField.val()
      };
   }
};

Then, we can use $.ajaxPrefilter to insert it into the header:
$.ajaxPrefilter(
   function (options, localOptions, jqXHR) {
      var token = GetAntiForgeryToken();
      jqXHR.setRequestHeader(token.name, token.value);
   }
);

I wrote a post about it here. Hope this helps!

Method 3

Use AntiForgeryToken with Ajax.ActionLink

In addition to jjwhite01 response;
to insert the token in Form data, use option.data in Prefilter

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

Method 4

I don’t know about the AJAX ActionLink specifically, but it is possible from a WebForms page to post to an MVC action with the [AcceptVerbs(HttpVerbs.Post), ValidateAntiForgeryToken] attributes.

You can use reflection to get at the MVC methods used to set the cookie and matching form input used for the MVC validation.

See this answer: Using an MVC HtmlHelper from a WebForm

Method 5

I haven’t used any ajax helpers myself, but I don’t see any reason why you cannot use a link. Personally I would use an onload event handler to unobtrusively create a link from the form itself, and then remove the form.

Method 6

To piggyback on the $.ajaxPrefilter answers, I added the token to both options and originalOptions rather than the jqXHR headers. This does require the token to be somewhere in a form on your page.

$.ajaxPrefilter(function (options, originalOptions, jqXHR) {
    var token = $('input[name="__RequestVerificationToken"]');
    if (token.length > 0) {
        var data = options.data;
        var dataArray = originalOptions.data;
        if (data && !data.includes('__RequestVerificationToken')) {
            options.data = data + '&__RequestVerificationToken=' + token.val();
        }
        if (dataArray && !('__RequestVerificationToken' in dataArray)) {
            var tokenObject = { name: '__RequestVerificationToken', value: token.val() };
            originalOptions.data.push(tokenObject);
        }
    }
});

Keep in mind that this will add this token to every single AJAX request on your page, so you may want to filter by the options.url string or options.type == 'POST'.


All methods was sourced from stackoverflow.com or stackexchange.com, is licensed under cc by-sa 2.5, cc by-sa 3.0 and cc by-sa 4.0

0 0 votes
Article Rating
Subscribe
Notify of
guest

0 Comments
Inline Feedbacks
View all comments
0
Would love your thoughts, please comment.x
()
x