ASP.NET CORE – Smart Way to Prevent Cross-Site Request Forgery (CSRF) Attempts – Protect AJAX XHR Requests
Thursday, August 18th, 2022ASP.NET CORE MVC – Protect AJAX Requests from CSRF Attempts
This is a follow-up post related to https://blog.eamster.tk/asp-net-mvc-smart-way-to-prevent-cross-site-request-forgery-csrf-attempts-webapi-ajax-xhr-and-normal-post-operations/
I've modified the code from the linked post above so that it works with ASP.NET CORE. Below is the code that can protect all AJAX requests from CSRF (Cross-Site Request Forgery) attempts. For normal <form> POST requests, you should still use and validate against a CSRF token, but if your application is separated into multiple pieces (for example a node.js React front-end application and a .NET CORE based API), this is an easy way to help prevent CSRF attacks without the use of tokens.
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Filters;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
namespace AnalyticsAPI.Filters
{
public class CSRFAjaxRequestMitigation : IAuthorizationFilter
{
public void OnAuthorization(AuthorizationFilterContext filterContext)
{
IServiceProvider services = filterContext.HttpContext.RequestServices;
IConfiguration Configuration = services.GetService<IConfiguration>();
string validOrigins = Configuration.GetValue<string>("AllowedEnvironments"); // Example in appsettings.json "AllowedEnvironments": "https://testurl.com:4443,https://testurl.com,https://testurl2.com",
bool skipCheck = false;
if(Configuration.GetValue<string>("ENVIRONMENT") == "LOCAL")
{
skipCheck = true;
}
// In AJAX requests, the origin header is always sent (UNLESS IT'S COMING FROM THE SAME ORIGIN), so we can validate that it comes from a trusted location to prevent CSRF attacks - but if one isn't sent, we won't do anything (assume trusted)
// In which case, we don't need to do any token checking either
if (!skipCheck && !string.IsNullOrEmpty(validOrigins))
{
List<string> validOriginURLs = validOrigins.Split(',').ToList();
if (!string.IsNullOrEmpty(filterContext.HttpContext.Request.Headers["Origin"].ToString()))
{
string origin = filterContext.HttpContext.Request.Headers["Origin"];
if (!validOriginURLs.Contains(origin, StringComparer.OrdinalIgnoreCase))
{
filterContext.Result = new UnauthorizedResult();
}
}
}
}
}
public class CSRFMitigationAttribute : TypeFilterAttribute
{
public CSRFMitigationAttribute()
: base(typeof(CSRFAjaxRequestMitigation))
{
Arguments = new object[] {};
}
}
}
