Lor*_*nzo 198 ajax asp.net-mvc csrf antiforgerytoken asp.net-mvc-2
我已经在我的应用程序中实现了对我在互联网上的一些博客文章中阅读的信息后的CSRF攻击的缓解.特别是这些帖子是我实施的驱动力
基本上这些文章和建议说,为了防止CSRF攻击,任何人都应该实现以下代码:
1)添加[ValidateAntiForgeryToken]接受POST Http动词的每个动作
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult SomeAction( SomeModel model ) {
}
Run Code Online (Sandbox Code Playgroud)
2)在<%= Html.AntiForgeryToken() %>向服务器提交数据的表单中添加帮助程序
<div style="text-align:right; padding: 8px;">
<%= Html.AntiForgeryToken() %>
<input type="submit" id="btnSave" value="Save" />
</div>
Run Code Online (Sandbox Code Playgroud)
无论如何,在我的应用程序的某些部分,我正在使用jQuery对服务器进行Ajax POST,而根本没有任何形式.例如,当我让用户点击图像来执行特定操作时,会发生这种情况.
假设我有一个包含活动列表的表.我在表的一列上有一个图像,上面写着"将活动标记为已完成",当用户点击该活动时,我正在进行Ajax POST,如下例所示:
$("a.markAsDone").click(function (event) {
event.preventDefault();
$.ajax({
type: "post",
dataType: "html",
url: $(this).attr("rel"),
data: {},
success: function (response) {
// ....
}
});
});
Run Code Online (Sandbox Code Playgroud)
我如何<%= Html.AntiForgeryToken() %>在这些情况下使用?我应该在Ajax调用的data参数中包含帮助器调用吗?
对不起,很长的帖子,非常感谢你的帮助
编辑:
根据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) {
// ....
}
});
});
Run Code Online (Sandbox Code Playgroud)
Jer*_*eir 241
我使用像这样的简单js函数
AddAntiForgeryToken = function(data) {
data.__RequestVerificationToken = $('#__AjaxAntiForgeryForm input[name=__RequestVerificationToken]').val();
return data;
};
Run Code Online (Sandbox Code Playgroud)
由于页面上的每个表单都具有相同的令牌值,因此只需将这样的内容放在最顶层的母版页中即可
<%-- used for ajax in AddAntiForgeryToken() --%>
<form id="__AjaxAntiForgeryForm" action="#" method="post"><%= Html.AntiForgeryToken()%></form>
Run Code Online (Sandbox Code Playgroud)
然后在你的ajax调用do(编辑以匹配你的第二个例子)
$.ajax({
type: "post",
dataType: "html",
url: $(this).attr("rel"),
data: AddAntiForgeryToken({ id: parseInt($(this).attr("title")) }),
success: function (response) {
// ....
}
});
Run Code Online (Sandbox Code Playgroud)
Bro*_*onx 29
我喜欢360Airwalk提供的解决方案,但可能会有所改进.
第一个问题是,如果$.post()使用空数据,jQuery不会添加Content-Type标头,在这种情况下,ASP.NET MVC无法接收和检查令牌.所以你必须确保标题始终存在.
另一个改进是支持所有带有内容的 HTTP动词:POST,PUT,DELETE等.虽然你的应用程序中只能使用POST,但最好有一个通用的解决方案并验证你用任何动词收到的所有数据都有防伪令牌.
$(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);
}
}
});
});
Run Code Online (Sandbox Code Playgroud)
vig*_*ity 22
我知道还有很多其他的答案,但这篇文章很简洁,并且强迫你检查所有的HttpPosts,而不仅仅是其中一些:
http://richiban.wordpress.com/2013/02/06/validating-net-mvc-4-anti-forgery-tokens-in-ajax-requests/
它使用HTTP标头而不是尝试修改表单集合.
//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);
}
}
}
}
Run Code Online (Sandbox Code Playgroud)
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 () {
...
}
});
Run Code Online (Sandbox Code Playgroud)
Edw*_*rey 19
不要使用Html.AntiForgeryToken.而是使用Web API中的AntiForgery.GetTokens和AntiForgery.Validate,如防止跨站请求伪造(CSRF)攻击中所述.
Wil*_*l D 19
我觉得这里是一名先进的死灵法师,但这仍然是4年后MVC5的一个问题.
要正确处理ajax请求,需要在ajax调用时将防伪令牌传递给服务器.将它集成到您的帖子数据和模型中是混乱和不必要的.将令牌添加为自定义标头是干净且可重复使用的 - 您可以对其进行配置,这样您就不必每次都记得这样做.
有一个例外 - Unobtrusive ajax不需要特殊处理ajax调用.令牌在常规隐藏输入字段中照常传递.与常规POST完全相同.
在_layout.cshtml中我有这个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>
Run Code Online (Sandbox Code Playgroud)
请注意在beforeSend函数中使用单引号 - 呈现的输入元素使用双引号来破坏JavaScript文字.
执行此操作时,将调用上面的beforeSend函数,并自动将AntiForgeryToken添加到请求标头中.
$.ajax({
type: "POST",
url: "CSRFProtectedMethod",
dataType: "json",
contentType: "application/json; charset=utf-8",
success: function (data) {
//victory
}
});
Run Code Online (Sandbox Code Playgroud)
处理非标准令牌需要自定义属性.这是基于@ 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);
}
}
}
}
Run Code Online (Sandbox Code Playgroud)
现在,您只需将该属性应用于您的Action.更好的是,您可以将属性应用于控制器,并验证所有请求.
[HttpPost]
[ValidateAntiForgeryTokenOnAllPosts]
public virtual ActionResult CSRFProtectedMethod()
{
return Json(true, JsonRequestBehavior.DenyGet);
}
Run Code Online (Sandbox Code Playgroud)
360*_*alk 16
我刚刚在我当前的项目中实现了这个实际问题.我为所有需要经过身份验证的用户的ajax-POST做了这件事.
首先,我决定挂钩我的jquery ajax调用,所以我不要经常重复自己.此javascript代码段确保所有ajax(post)调用都将我的请求验证令牌添加到请求中.注意:名称__RequestVerificationToken由.Net框架使用,因此我可以使用标准的Anti-CSRF功能,如下所示.
$(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);
}
}
});
});
Run Code Online (Sandbox Code Playgroud)
在您的视图中,您需要令牌可用于上述javascript,只需使用常见的HTML-Helper.您基本上可以随意添加此代码.我将它放在if(Request.IsAuthenticated)语句中:
@Html.AntiForgeryToken() // you can provide a string as salt when needed which needs to match the one on the controller
Run Code Online (Sandbox Code Playgroud)
在您的控制器中,只需使用标准的ASP.Net MVC Anti-CSRF机制.我这样做了(虽然我实际上使用了Salt).
[HttpPost]
[Authorize]
[ValidateAntiForgeryToken]
public JsonResult SomeMethod(string param)
{
// do something
return Json(true);
}
Run Code Online (Sandbox Code Playgroud)
使用Firebug或类似工具,您可以轻松查看POST请求现在如何附加__RequestVerificationToken参数.
jba*_*all 14
我认为您所要做的就是确保POST请求中包含"__RequestVerificationToken"输入.另一半信息(即用户cookie中的令牌)已经通过AJAX POST请求自动发送.
例如,
$("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) {
// ....
}
});
});
Run Code Online (Sandbox Code Playgroud)
你也可以这样做:
$("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) {
// ....
}
});
});
Run Code Online (Sandbox Code Playgroud)
这是使用Razor,但如果你使用WebForms语法,你也可以使用<%= %>标签
| 归档时间: |
|
| 查看次数: |
138171 次 |
| 最近记录: |