If you use cookie-based authentication, then you are susceptible to XSRF / CSRF
. The ASP.NET Core
documentation has an article dedicated to preventing Cross-Site Request Forgery.
The goal of this guide is to demonstrate how to mitigate CSRF
for a SPA
using Angular 6.x
and being the only consumer of an API
using ASP.NET Core 2.1
.
🚨 you need to configure data protection in order to be able to use antiforgery 🚨
A common pattern for SPA
applications is to retrieve some configuration via an initial call. In this example the configuration endpoint is available at /api/spa/environment
. As it is recommended to refresh the antiforgery tokens after authentication, we will set the antiforgery tokens when retrieving the configuration. We are doing this via a middleware to give us the flexibility to move it to another endpoint (or endpoints) if required.
public class AngularAntiforgeryMiddleware
{
// https://angular.io/guide/http#security-xsrf-protection
public static string AngularCookieName = "XSRF-TOKEN";
public static string AngularHeaderName = "X-XSRF-TOKEN";
private readonly RequestDelegate _next;
public AngularAntiforgeryMiddleware(RequestDelegate next)
{
_next = next ?? throw new ArgumentNullException(nameof(next));
}
public Task InvokeAsync(
HttpContext httpContext,
IAntiforgery antiforgery,
ILogger<AngularAntiforgeryMiddleware> logger)
{
if (httpContext == null) throw new ArgumentNullException(nameof(httpContext));
if (httpContext.Request.Path.StartsWithSegments("/api/spa/environment"))
{
logger.LogDebug("Writing antiforgery cookies");
// This method provided by the framework writes a cookie that will be used
// to validate the request (e.g. the cookie token should match the request
// token)
var tokens = antiforgery.GetAndStoreTokens(httpContext);
// We need to write the cookie that will be used by Angular to set the
// HTTP header
httpContext.Response.Cookies.Append(
AngularCookieName,
tokens.RequestToken,
new CookieOptions
{
HttpOnly = false,
Secure = true,
SameSite = SameSiteMode.Strict
});
}
return _next(httpContext);
}
}
The other modifications take place in the Startup.cs
file
public class Startup
{
public void ConfigureServices(IServiceCollection services)
{
// Abbreviated
services
.AddMvc(c =>
{
c.Filters.Add(new AutoValidateAntiforgeryTokenAttribute());
})
.AddAntiforgery(options =>
{
options.HeaderName = AngularAntiforgeryMiddleware.AngularHeaderName;
options.Cookie.SecurePolicy = CookieSecurePolicy.Always;
});
// Abbreviated
}
public void Configure(IApplicationBuilder app)
{
// Abbreviated
app.UseMiddleware<AngularAntiforgeryMiddleware>();
// Abbreviated
}
}
AutoValidateAntiforgeryTokenAttribute will validate all requests except for the following methods: HEAD
, TRACE
, OPTIONS
and GET
.
Nothing needs to be done in the SPA
as we've named the header and cookie following the Angular
convention ✨
If you encounter issues with seemingly valid antiforgery tokens being rejected by ASP.NET Core
you can set the verbosity of Microsoft.AspNetCore.Mvc.ViewFeatures.Internal.AutoValidateAntiforgeryTokenAuthorizationFilter
to Information
. Log events will be emitted at this level only when the validation fails.