How to use Anti-Forgery Token With ASP.NET Web API without ASP.NET MVC?
Stephen Walther has this article of "Preventing Cross-Site Request Forgery Attacks with ASP.NET MVC" in http://stephenwalther.com/archive/2013/03/05/security-issues-with-single-page-apps ... but his solution includes MVC/Razor and in my front-end I don't planning to include it. And there are abundance articles like it, which the solution is adding @Html.AntiForgeryToken()
but this cannot be my solution.
Later, I solved another issue, "the Same Origin policy": http://www.asp.net/web-api/overview/security/enabling-cross-origin-requests-in-web-api, Could this be the solution to prevent the CSRF as well? I don't think so.
The automatic generation of antiforgery tokens for HTML form elements happens when the <form> tag contains the method="post" attribute and either of the following are true: The action attribute is empty ( action="" ). The action attribute isn't supplied ( <form method="post"> ).
Cross-site request forgery (CSRF) is an attack that tricks an end user into executing undesirable actions while logged into a web application. Taking advantage of the authenticated user's permissions, a CSRF attack dupes the victim into performing specific actions that benefit the attacker.
As given in Wikipedia (https://en.wikipedia.org/wiki/Cross-site_request_forgery/), Cross-site request forgery (CSRF or XSRF), also known as a one-click attack or session riding, is a type of malicious exploit of a website whereby unauthorized commands are transmitted from a user that the website trusts.
The token is stored in a hidden form field and in a cookie, separate from a cookie session (you may find details here). Additionally, when anti-forgery token is invalid, an exception is thrown so it does not look like the problem you have is connected with anti-forgery tokens.
My problem was that I did not want to use MVC and just serve static html files backed by a WebApi. Here is what I did (Would this work?) Create a Http module that sets a random cookie value when serving any static file. For example:
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()));
}
}
}
Then in your html page, use javascript to add the cookie value to a header when calling your api:
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();
}
finally, in your HttpModule
, check that the cookie matches the header before processing any calls to your api:
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();
}
}
}
You need to set the HttpOnly
flag to FALSE
so that javascript from your domain can read the cookie and set the header. I am not a security expert, so I would like some feedback on this solution from some other members of the community.
EDIT
If you are using OWIN, you can use a global action Filter along with a middleware plugin:
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";
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With