ASP.NET Core — Angular — XSRF
I’ve created a template specifically for this demo with some pages already in the angular app:
git clone https://github.com/PieterjanDeClippel/XsrfDemo
Open the solution inside the folder.
Refactoring
We’ll start by refactoring the application a bit so that we can also create/update weatherforecasts.
Add the WeatherForecastStore. This is just a wrapper around a List. In real life this would inject a DbContext.
This requires an Id
property on the WeatherForecast
Register the store as a Singleton Service:
And call the Seed method:
Now modify the existing api controller, and move it to Controllers/Web:
Add a Create button on top of the weatherforecast overview page:
Now generate a page to add a weatherforecast
cd app/pages/fetch-data
ng g module create --module fetch-data --route add
Import the FormsModule
in the create.module.ts
Update the code of the create.component.ts:
Change the WeatherForecast.ts
date field to a Date | null
.
You can now add + get weather forecasts. Note that we don’t have [Authorize]
attributes on the controller methods.
Adding the Antiforgery services
Go to the Program.cs
file and add the following line:
This will verify if an XSRF token is present on each POST/PUT/DELETE request decorated with the [ValidateAntiForgeryToken]
attribute. Add the attribute to the Create, Update and Delete methods of the weatherforecast controller.
If you run the application and try to add a forecast, you’ll get the following:
This is because the requests don’t contain a X-XSRF-TOKEN
header. Since we’re dealing with a single-page application, we must find a way to return such a token without requiring a page reload. This is done through a javascript cookie. I’ve created a package containing a middleware which returns a cookie with an XSRF-TOKEN.
Open the NuGet package manager and install MintPlayer.AspNetCore.SpaServices.Xsrf
in the project.
Add the following line right after the UseHttpsRedirection()
middleware:
app.UseAntiforgery();
ctrl+; gives you the suggestion to add the necessary using.
This middleware generates an AntiForgery token and returns it to the angular app through a XSRF-TOKEN
cookie (non HttpOnly) on each webrequest.
Now we must instruct the angular app to read this token from the cookie, and send it along in every request as a XSRF-TOKEN
header:
Now we can test the application, but you’ll still see the 400 error:
Notice that no XSRF-TOKEN header is being sent by angular:
This is because angular doesn’t send the header when the url starts with http: or https:. So we must remove it from the base-url. Open main.ts
and modify the getBaseUrl
function:
Now the XSRF-token is being sent along as X-XSRF-TOKEN
header, and .NET won’t block your webrequest anymore.
A huge exclamation mark
In MVC webapplications XSRF tokens are generated and included whenever a <form>
is returned to the user. A hacker can host his own website http://bad-website.com
with a form pointing to your website
<form action="http://my-website.com/posts/create" method="POST">
<input type="hidden" name="content" value="I pooped my pants">
<input type="submit" value="You won € 100.000. Click here to collect it">
<input type="hidden" name="XSRF_TOKEN" id="token" value="I_would_send_it_if_i_could_retrieve_it_somehow">
</form>
<script>
$.get("http://my-website.com/posts/create", function (response) {
const token = processResponseGetXsrfToken(response);
$("#token").val(token);
});
</script>
Your website verifies that the XSRF token corresponds to the authenticated user.
It’s pointless to setup all of the above when pages serving forms to users can be requested from other websites. This essentially means that each page serving a form with an XSRF-token must never serve an Access-Control-Allow-Origin
response header.
In single-page-applications, we send an XSRF-token through a javascript-cookie, so other websites must not be able to read this cookie.
Appendix
Please, FFS, use Cookie authentication in your angular app!!!! Your angular app runs in the webbrowser, and ever since the 90’s, cookie authentication is specifically designed to be used by browsers. This walkthrough is an explicit POC of how to use Cross-site request forgery in SPA’s, so use it. The key is
Have I been clear? Let me know in the comments… 😊
The demo can be found in this repository. The library with the Antiforgery middleware, which generates an XSRF-token and attaches the cookie to the HTTP-responses, is hosted here.
Add api controllers
Alas, if you need to develop a mobile app with authentication, you can now add API-controllers which use bearer authentication. A fully-fledged code-repository of an application
- implementing XSRF protection in the web controllers
- using Cookie authentication on the web controllers (for your angular app)
- using Bearer authentication on the api controllers (for your mobile app)
is available here.