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
-
Best Practices for ASP.NET MVC from the ASP.NET and Web Tools Developer Content Team
-
Anatomy of a Cross-site Request Forgery Attack from Phil Haack blog
-
AntiForgeryToken in the ASP.NET MVC Framework - Html.AntiForgeryToken and ValidateAntiForgeryToken Attribute from David Hayden blog
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
<div style="text-align:right; padding: 8px;">
<%= Html.AntiForgeryToken() %>
<input type="submit" id="btnSave" value="Save" />
</div>
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) {
// ....
}
});
});
Source: Tips4all
I use a simple js function like this
ReplyDeleteAddAntiForgeryToken = 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) {
// ....
}
});
You can do this also:
ReplyDelete$("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
i was just implementing this actual problem in my current project. i did it for all ajax-POSTs that needed an authenticated user.
ReplyDeleteFirst 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 () {
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.
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.
ReplyDeleteE.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) {
// ....
}
});
});
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
ReplyDeleteSolved 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
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:
ReplyDeleteAnti-Forgery Request Recipes For ASP.NET MVC And AJAX
I'm not contributing much, but maybe someone will find it useful.