我已经在我的应用程序中实现了对CSRF攻击的缓解,这是我在互联网上的一些博客文章上读到的信息。特别是这些帖子一直是我实现的驱动力
ASP的最佳实践。NET MVC从ASP。NET和Web工具开发人员内容团队
跨站点请求伪造攻击的剖析,来自Phil Haack的博客
ASP中的AntiForgeryToken。NET MVC框架- Html。来自David Hayden博客的AntiForgeryToken和ValidateAntiForgeryToken属性
基本上,这些文章和建议说,为了防止CSRF攻击,任何人都应该实现以下代码:
在接受POST Http谓词的每个操作上添加[ValidateAntiForgeryToken]
(HttpPost)
(ValidateAntiForgeryToken)
SomeAction(SomeModel模型){
}
在向服务器提交数据的表单中添加<%= Html.AntiForgeryToken() %> helper
无论如何,在我的应用程序的某些部分,我做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调用的数据参数中包含helper调用?
很抱歉写了这么长时间,非常感谢你的帮助
编辑:
根据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) {
// ....
}
});
});
除了我对@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');
}
});
我保留了“success”动作,因为它显示了包含MvcJqGrid的部分视图是如何被更新的,以及它是如何被刷新的(非常强大的jqGrid网格,这是一个出色的MVC包装器)。
我的控制器方法是这样的:
//Ajax SUBMIT method
[ValidateAntiForgeryToken]
public ActionResult SearchAjax(EstateOutlet_D model)
{
return View("_Grid", model);
}
我必须承认,我不喜欢将整个表单的数据作为模型发布,但如果您需要这样做,那么这是一种可行的方法。MVC只是让数据绑定太容易,而不是提交16个单独的值(或一个弱类型的FormCollection),这是可以的,我猜。如果你知道更好,请告诉我,因为我想产生健壮的MVC c#代码。
在这里我感觉自己像个高级的死灵法师,但这在MVC5四年后仍然是一个问题。
为了正确地处理ajax请求,需要在ajax调用时将防伪造令牌传递给服务器。将它集成到你的post数据和模型中是混乱和不必要的。将令牌作为自定义标头添加是干净且可重用的—您可以对其进行配置,这样就不必每次都记得这样做。
有一个例外- Unobtrusive ajax不需要ajax调用的特殊处理。令牌像往常一样在常规隐藏输入字段中传递。和普通POST完全一样。
_Layout.cshtml
在_layout。我有这个JavaScript块。它不将令牌写入DOM,而是使用jQuery从MVC Helper生成的隐藏输入文本中提取令牌。作为头名称的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);
}
}
}
}
服务器/控制器
现在只需将属性应用到Action。更好的是,你可以将属性应用到你的控制器,所有的请求都将被验证。
[HttpPost]
[ValidateAntiForgeryTokenOnAllPosts]
public virtual ActionResult CSRFProtectedMethod()
{
return Json(true, JsonRequestBehavior.DenyGet);
}