AngularJS Web Api AntiForgeryToken CSRF
Add to the ASP.NET MVC View that serves the AngularJS SPA, let's say
Views\Home\Index.cshtml
, the HTML helper that generates theAntiForgeryToken
.
@Html.AntiForgeryToken();
Configure AngularJS to pass the above generated
AntiForgeryToken
as Request Header.
angular.module('app')
.run(function ($http) {
$http.defaults.headers.common['X-XSRF-Token'] =
angular.element('input[name="__RequestVerificationToken"]').attr('value');
});
Create a custom Web API Filter to validate all non-GET requests (
PUT
,PATCH
,POST
,DELETE
).This assumes that all your
GET
requests are safe and don't need protecting.
If that's not the case, remove theif (actionContext.Request.Method.Method != "GET")
exclusion.
using System;
using System.Linq;
using System.Net.Http;
using System.Web.Helpers;
using System.Web.Http.Filters;
namespace Care.Web.Filters
{
public sealed class WebApiValidateAntiForgeryTokenAttribute : ActionFilterAttribute
{
public override void OnActionExecuting(
System.Web.Http.Controllers.HttpActionContext actionContext)
{
if (actionContext == null)
{
throw new ArgumentNullException("actionContext");
}
if (actionContext.Request.Method.Method != "GET")
{
var headers = actionContext.Request.Headers;
var tokenCookie = headers
.GetCookies()
.Select(c => c[AntiForgeryConfig.CookieName])
.FirstOrDefault();
var tokenHeader = string.Empty;
if (headers.Contains("X-XSRF-Token"))
{
tokenHeader = headers.GetValues("X-XSRF-Token").FirstOrDefault();
}
AntiForgery.Validate(
tokenCookie != null ? tokenCookie.Value : null, tokenHeader);
}
base.OnActionExecuting(actionContext);
}
}
}
Register the newly created filter as a global one, in
Global.asax.cs
.
private static void RegisterWebApiFilters(HttpFilterCollection filters)
{
filters.Add(new WebApiValidateAntiForgeryTokenAttribute());
}
Alternatively, if you don't wish to add this filter globally, you can put it only on certain Web API actions, like this
[WebApiValidateAntiForgeryToken]
This, of course, is by definition less secure, as there's always the chance that you'll forget to apply the attribute to an action that needs it.
Also, note that you must have the Microsoft.AspNet.WebApi.Core
package in order to have access to System.Web.Http
namespace. You can install it via NuGet with Install-Package Microsoft.AspNet.WebApi.Core
.
This post has been heavily inspired by this blog post.