我在做基于JSON的AJAX请求,并与MVC控制器一直很感激菲尔哈克他与AJAX预防CSRF和,约翰·德里森的更新的防XSRF为MVC 4 RC 。 但是,正如我过渡API为核心控制器的Web API,我打的问题,其中的两种方法之间的功能是明显不同的,我无法过渡CSRF代码。
斯科特提出了类似的问题,最近这是回答由达林季米特洛夫。 Darin的解决方案涉及实施授权过滤器调用AntiForgery.Validate。 不幸的是,这个代码不为我工作(见下文)和 - 诚实 - 对我来说太先进。
据我了解,菲尔的解决方案克服使得在没有表单元素的JSON请求时,MVC防伪问题; 表单元素假定/由AntiForgery.Validate方法预期。 我认为 ,这可能是为什么我在与达林的解决的问题了。 我收到HttpAntiForgeryException“所需的防伪造表单字段‘__RequestVerificationToken’不存在”。 我确信,该令牌被张贴(尽管每菲尔哈克的解决方案的标题)。 这里的客户端调用的快照:
$token = $('input[name=""__RequestVerificationToken""]').val();
$.ajax({
url:/api/states",
type: "POST",
dataType: "json",
contentType: "application/json: charset=utf-8",
headers: { __RequestVerificationToken: $token }
}).done(function (json) {
...
});
我尝试了黑客通过一起捣碎约翰与达林的解决方案,并能得到的东西的工作,但我引入HttpContext.Current,这个不确定是否适当/安全的,为什么我不能使用提供HttpActionContext。
这是我的不雅混搭..变化是2号线在try块:
public Task<HttpResponseMessage> ExecuteAuthorizationFilterAsync(HttpActionContext actionContext, CancellationToken cancellationToken, Func<Task<HttpResponseMessage>> continuation)
{
try
{
var cookie = HttpContext.Current.Request.Cookies[AntiForgeryConfig.CookieName];
AntiForgery.Validate(cookie != null ? cookie.Value : null, HttpContext.Current.Request.Headers["__RequestVerificationToken"]);
}
catch
{
actionContext.Response = new HttpResponseMessage
{
StatusCode = HttpStatusCode.Forbidden,
RequestMessage = actionContext.ControllerContext.Request
};
return FromResult(actionContext.Response);
}
return continuation();
}
我的问题是:
- 我在想,达林的方案假设一个表单元素的存在是否正确?
- 什么是混搭了约翰的MVC 4 RC码达林的Web API过滤器优雅的方式?
提前致谢!
你可以尝试从标题写着:
var headers = actionContext.Request.Headers;
var cookie = headers
.GetCookies()
.Select(c => c[AntiForgeryConfig.CookieName])
.FirstOrDefault();
var rvt = headers.GetValues("__RequestVerificationToken").FirstOrDefault();
AntiForgery.Validate(cookie != null ? cookie.Value : null, rvt);
注意: GetCookies
是存在于类的扩展方法HttpRequestHeadersExtensions
其是部分System.Net.Http.Formatting.dll
。 它将最有可能存在C:\Program Files (x86)\Microsoft ASP.NET\ASP.NET MVC 4\Assemblies\System.Net.Http.Formatting.dll
只是想补充一点,这种方法为我工作也(阿贾克斯JSON发布到Web API端点),虽然我从ActionFilterAttribute继承和重写OnActionExecuting方法简化了一点。
public class ValidateJsonAntiForgeryTokenAttribute : ActionFilterAttribute
{
public override void OnActionExecuting(HttpActionContext actionContext)
{
try
{
var cookieName = AntiForgeryConfig.CookieName;
var headers = actionContext.Request.Headers;
var cookie = headers
.GetCookies()
.Select(c => c[AntiForgeryConfig.CookieName])
.FirstOrDefault();
var rvt = headers.GetValues("__RequestVerificationToken").FirstOrDefault();
AntiForgery.Validate(cookie != null ? cookie.Value : null, rvt);
}
catch
{
actionContext.Response = actionContext.Request.CreateErrorResponse(HttpStatusCode.Forbidden, "Unauthorized request.");
}
}
}
使用Darin的答案,与头的存在的核对扩展方法。 支票是指所产生的错误信息是更表明了什么问题(“所需的防伪型窗体域‘__RequestVerificationToken’是不存在的。”)与“给出的标题没有被发现。”
public static bool IsHeaderAntiForgeryTokenValid(this HttpRequestMessage request)
{
try
{
HttpRequestHeaders headers = request.Headers;
CookieState cookie = headers
.GetCookies()
.Select(c => c[AntiForgeryConfig.CookieName])
.FirstOrDefault();
var rvt = string.Empty;
if (headers.Any(x => x.Key == AntiForgeryConfig.CookieName))
rvt = headers.GetValues(AntiForgeryConfig.CookieName).FirstOrDefault();
AntiForgery.Validate(cookie != null ? cookie.Value : null, rvt);
}
catch (Exception ex)
{
LogHelper.LogError(ex);
return false;
}
return true;
}
ApiController用法:
public IHttpActionResult Get()
{
if (Request.IsHeaderAntiForgeryTokenValid())
return Ok();
else
return BadRequest();
}
使用AuthorizeAttribute的实现:
using System;
using System.Linq;
using System.Net.Http;
using System.Web;
using System.Web.Helpers;
using System.Web.Http;
using System.Web.Http.Controllers;
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = false, Inherited = true)]
public class ApiValidateAntiForgeryToken : AuthorizeAttribute {
public const string HeaderName = "X-RequestVerificationToken";
private static string CookieName => AntiForgeryConfig.CookieName;
public static string GenerateAntiForgeryTokenForHeader(HttpContext httpContext) {
if (httpContext == null) {
throw new ArgumentNullException(nameof(httpContext));
}
// check that if the cookie is set to require ssl then we must be using it
if (AntiForgeryConfig.RequireSsl && !httpContext.Request.IsSecureConnection) {
throw new InvalidOperationException("Cannot generate an Anti Forgery Token for a non secure context");
}
// try to find the old cookie token
string oldCookieToken = null;
try {
var token = httpContext.Request.Cookies[CookieName];
if (!string.IsNullOrEmpty(token?.Value)) {
oldCookieToken = token.Value;
}
}
catch {
// do nothing
}
string cookieToken, formToken;
AntiForgery.GetTokens(oldCookieToken, out cookieToken, out formToken);
// set the cookie on the response if we got a new one
if (cookieToken != null) {
var cookie = new HttpCookie(CookieName, cookieToken) {
HttpOnly = true,
};
// note: don't set it directly since the default value is automatically populated from the <httpCookies> config element
if (AntiForgeryConfig.RequireSsl) {
cookie.Secure = AntiForgeryConfig.RequireSsl;
}
httpContext.Response.Cookies.Set(cookie);
}
return formToken;
}
protected override bool IsAuthorized(HttpActionContext actionContext) {
if (HttpContext.Current == null) {
// we need a context to be able to use AntiForgery
return false;
}
var headers = actionContext.Request.Headers;
var cookies = headers.GetCookies();
// check that if the cookie is set to require ssl then we must honor it
if (AntiForgeryConfig.RequireSsl && !HttpContext.Current.Request.IsSecureConnection) {
return false;
}
try {
string cookieToken = cookies.Select(c => c[CookieName]).FirstOrDefault()?.Value?.Trim(); // this throws if the cookie does not exist
string formToken = headers.GetValues(HeaderName).FirstOrDefault()?.Trim();
if (string.IsNullOrEmpty(cookieToken) || string.IsNullOrEmpty(formToken)) {
return false;
}
AntiForgery.Validate(cookieToken, formToken);
return base.IsAuthorized(actionContext);
}
catch {
return false;
}
}
}
然后,只需装点您的控制器或与方法[ApiValidateAntiForgeryToken]
并加入到剃刀这个文件来生成JavaScript的令牌:
<script>
var antiForgeryToken = '@ApiValidateAntiForgeryToken.GenerateAntiForgeryTokenForHeader(HttpContext.Current)';
// your code here that uses such token, basically setting it as a 'X-RequestVerificationToken' header for any AJAX calls
</script>
如果有帮助的人,在.NET核心,标头的默认值实际上只是“RequestVerificationToken”,没有“__”。 所以,如果你改变标题的关键是,相反,它会工作。
您也可以覆盖头名称,如果你喜欢:
services.AddAntiforgery(o => o.HeaderName = "__RequestVerificationToken")
文章来源: Problems implementing ValidatingAntiForgeryToken attribute for Web API with MVC 4 RC