如何在没有ASP.NET MVC的情况下使用带有ASP.NET Web API的防伪标记?
Stephen Walther在http://stephenwalther.com/archive/2013/03/05/security-issues-with-single-page-apps中有一篇“用ASP.NET MVC防止跨站点请求伪造攻击”的文章......但他的解决方案包括MVC / Razor,在我的前端我没有计划包括它。并且有很多类似的文章,解决方案正在添加@Html.AntiForgeryToken()
,但这不是我的解决方案。
后来,我解决了另一个问题,“同源政策”:http://www.asp.net/web-api/overview/security/enabling-cross-origin-requests-in-web-api,这可能是防止CSRF的解决方案吗?我不这么认为。
答案 0 :(得分:3)
我的问题是我不想使用MVC而只是提供由WebApi支持的静态html文件。这是我做的(这会工作吗?)创建一个Http模块,在提供任何静态文件时设置一个随机cookie值。例如:
public class XSRFModule : IHttpModule {
...
void context_EndRequest(object sender, EventArgs e) {
if (Path.GetExtension(HttpContext.Current.Request.Path) == ".html") {
HttpContext.Current.Response.Cookies.Add(new HttpCookie("XSRF-TOKEN", Guid.NewGuid().ToString()));
}
}
}
然后在你的html页面中,使用javascript在调用你的api时将cookie值添加到标题中:
function callApi() {
xhr = new XMLHttpRequest();
xhr.open("GET", "api/data", true);
var regex = /\b(?:XSRF-TOKEN=)(.*?)(?=\s|$)/
var match = regex.exec(document.cookie);
xhr.setRequestHeader("X-XSRF-TOKEN", match[1]);
xhr.send();
}
最后,在HttpModule
中,在处理对api的任何调用之前,检查Cookie是否与标题匹配:
void context_BeginRequest(object sender, EventArgs e)
{
if (HttpContext.Current.Request.Path.StartsWith("/api"))
{
string fromCookie = HttpContext.Current.Request.Cookies.Get("XSRF-TOKEN").Value;
string fromHeader = HttpContext.Current.Request.Headers["X-XSRF-TOKEN"];
if (fromCookie != fromHeader)
{
HttpContext.Current.Response.StatusCode = (int)HttpStatusCode.Forbidden;
HttpContext.Current.Response.End();
}
}
}
您需要将HttpOnly
标记设置为FALSE
,以便您域中的javascript可以读取Cookie并设置标头。我不是安全专家,所以我想从社区的其他成员那里得到一些关于此解决方案的反馈。
修改强>
如果您使用的是OWIN,则可以使用全局操作过滤器和中间件插件:
Startup.cs
app.UseStaticFiles(new StaticFileOptions {
OnPrepareResponse = (responseContext) => {
responseContext.OwinContext.Response.Cookies.Append("XSRF-TOKEN", Guid.NewGuid().ToString());
},
FileSystem = "wwwroot"
});
XsrfFilter.cs
public class XsrfFilter : ActionFilterAttribute {
public override void OnActionExecuting(System.Web.Http.Controllers.HttpActionContext actionContext) {
string fromCookie = actionContext.Request.Headers.GetCookies("XSRF-TOKEN").FirstOrDefault()["XSRF-TOKEN"].Value;
string fromHeader = actionContext.Request.Headers.GetValues("X-XSRF-TOKEN").FirstOrDefault();
if (fromCookie == fromHeader) return;
actionContext.Response = new HttpResponseMessage(HttpStatusCode.OK);
actionContext.Response.ReasonPhrase = "bad request";
}
}