ASP.NET CORE – Smart Way to Prevent Cross-Site Request Forgery (CSRF) Attempts – Protect AJAX XHR Requests

Thursday, August 18th, 2022

ASP.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[] {};
        }
    }
} 

 

ASP.NET Web API – Accessing Session Information

Monday, August 12th, 2019

ASP.NET Web API – Accessing Session Information

If WebAPI needs access to SESSION information, here's how to do it:

https://stackoverflow.com/questions/9594229/accessing-session-using-asp-net-web-api#answer-17539008 or ARCHIVE

ASP.NET MVC – Using a Global Controller Filter to Add Information to the ViewBag

Monday, August 12th, 2019

Using a Global Controller Filter to Add Information to the ViewBag

There are times where you may want to add information to the ViewBag in ASP.NET MVC that should be available to all of the views referenced within certain controllers.  For this situation, you can create a global filter that can be applied at the controller or action specific level to make certain information available to the view via the usage of the ViewBag.

Here's a basic filter example:

public class TestInformationFilter : ActionFilterAttribute
{
    public override void OnActionExceuting(ActionExecutingContext context){
        // Set ViewBag Vars
        context.Controller.ViewBag.UserFirstName = context.Session["FirstName"];
        
        // Complete normal actions
        base.OnActionExecuting(context);
    }
}

Register your global filter by editing the FilterConfig.cs file found in the App_Start folder like so:

public class FilterConfig{
    public static void RegisterGlobalFilters(GlobalFilterCollection filters){
        filters.Add(new TestInformationFilter());
    }
}

Make all views from a controller have access to this ViewBag information by applying the filter to the controller:

[TestInformationFilter]
public class MyController{
   // My controller code here
}