What is Cross Site Request Forgery (CSRF or XSRF)
There are numerous reasons why you should implement an Anti Forgery Token. With ASP.NET Core and Angular this is almost possible out of the box.
As I did not find any basic examples, most where creating a full webpage application, I have decided to write a small blog about my implementation in one of my projects.
This example is based on ASP.NET Core 1.0.0-rc1-update2 and uses AngularJS 1.4.6
From the AngularJS: API: $http page:
XSRF is an attack technique by which the attacker can trick an authenticated user into unknowingly executing actions on your website. Angular provides a mechanism to counter XSRF. When performing XHR requests, the $http service reads a token from a cookie (by default,
XSRF-TOKEN
) and sets it as an HTTP header (X-XSRF-TOKEN
). Since only JavaScript that runs on your domain could read the cookie, your server can be assured that the XHR came from JavaScript running on your domain. The header will not be set for cross-domain requests.To take advantage of this, your server needs to set a token in a JavaScript readable session cookie called
XSRF-TOKEN
on the first HTTP GET request. On subsequent XHR requests the server can verify that the cookie matchesX-XSRF-TOKEN
HTTP header, and therefore be sure that only JavaScript running on your domain could have sent the request. The token must be unique for each user and must be verifiable by the server (to prevent the JavaScript from making up its own tokens). We recommend that the token is a digest of your site’s authentication cookie with a salt for added security.The name of the headers can be specified using the xsrfHeaderName and xsrfCookieName properties of either $httpProvider.defaults at config-time, $http.defaults at run-time, or the per-request config object.
In order to prevent collisions in environments where multiple Angular apps share the same domain or subdomain, we recommend that each application uses unique cookie name.
The Tom Scott from Computerphile have a nice video about it as well.
Implementing the Anti Forgery Token
In our ASP.NET Core application we have to implement the following parts in the Startup.cs in order to send out these specified cookies
In the ConfigureServices part add the following two lines:
[code lang=”csharp”]
public void ConfigureServices(IServiceCollection services)
{
// Angular’s default header name for sending the XSRF token.
services.ConfigureAntiforgery(options => options.FormFieldName = "X-XSRF-TOKEN");
services.AddAntiforgery();
}
[/code]
In the Configure part add:
[code lang=”csharp”]
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory, IAntiforgery antiforgery)
{
app.Use(next => context =>
{
// We can send the request token as a JavaScript-readable cookie, and Angular will use it by default.
var tokens = antiforgery.GetAndStoreTokens(context);
context.Response.Cookies.Append("XSRF-TOKEN", tokens.FormToken, new CookieOptions() { HttpOnly = false });
var formFields = new Dictionary<string, StringValues>()
{
{"X-XSRF-TOKEN", context.Request.Headers["X-XSRF-TOKEN"]}
};
context.Request.Form = new FormCollection(formFields);
return next(context);
});
}
[/code]
In order to validate the token, add the ValidateAntiForgeryToken attribute to your controller.
You can also add this attribute to a method itself. I prefer to make a base controller and set the ValidateAntiForgeryToken attribute on that class in order to get in on every post/get around my webapplication.
[code lang=”csharp”]
[ValidateAntiForgeryToken]
public class HomeController : Controller
{
public IActionResult Index()
{
return View();
}
}
[/code]
Validating that it works
Lets fire up our webapplication and if we then look at what this does for us, we notice that we got two cookies (yummie)
In every next Angular http get or http post, Angular will send these back as well.
Not only that, also the XSRF-TOKEN cookie will be set in the header:
Upon every request, the ASP.NET Core environment will send you a new XSRF-TOKEN cookie in order to prevent a replay attack:
You can also see this in the Response Headers in the previous image where the Set-Cookie has a value.
If you try to only send the two cookies, the ASP.NET Core validation will fail and an error will occur:
We can validate this by opening the get response in a new tab, where the cookies are available but the X-XSR-TOKEN header is not set by Angular.
And of course, if you modify the cookies itself, you will get another error regardless of the content you are executing the following get from:
Using this method, when i send data from the angular side as formdata, it gets replaced during the request to the form field containing the token and i lose my data