Cross-Site Request Forgery (CSRF) in ASP.NET Core

CSRF is a type of web security vulnerability that forces authenticated users to submit unwanted requests to a web application.

It's a way for attackers to manipulate users into performing actions they didn't intend to, often leading to data theft, unauthorized actions, or even complete account compromise.

Simple ASP.NET Core Web Application

Web Application

It has two buttons.

  • "Card Details": Uses form to access card details.
  • "Make Ajax Call": Access card details with Ajax call.

Here is a simple controller to access user card details.

  1. The [ValidateAntiForgeryToken] attribute ensures that any request to this action method must include a valid anti-forgery token.
    [ValidateAntiForgeryToken]
    [HttpPost]
    public IActionResult Card(int id)
    {
        Card card = new Card() { CardNumber = "1234 5678 9012 3456", CVV = "123", ExpiryDate = "12/2022", Name = "John Doe", Id = id };
        return View(card);
    }
  2. Enable Antiforgery in ASP.NET Core: Antiforgery middleware is added to the Dependency injection container when one of the following APIs is called in the Program. cs.
    • AddMvc
    • MapRazorPages
    • MapControllerRoute
    • AddRazorComponents
  3. Customize Antiforgery Token: We can customize the anti-forgery token by configuring the anti-forgery options in the Program.cs file.
    builder.Services.AddAntiforgery(options =>
    {
        options.HeaderName = AntiforgeryMiddleware.HeaderName;
        options.FormFieldName = AntiforgeryMiddleware.FormFieldName;    
    });
  4. Implement Antiforgery token with Cookie: This should now protect your endpoints that are using POST, PUT, and DELETE HTTP verbs.
    public class AntiforgeryMiddleware
    {
        public const string HeaderName = "X-CSRF-TOKEN";
        public const string CookieName = "X-CSRF-TOKEN";
        public const string FormFieldName = "X-CSRF-TOKEN";
        private readonly RequestDelegate _next;
        private readonly IAntiforgery _antiforgery;
        public AntiforgeryMiddleware(RequestDelegate next, IAntiforgery antiforgery)
        {
            _next = next ?? throw new ArgumentNullException(nameof(next));
            _antiforgery = antiforgery;
        }
        public async Task Invoke(HttpContext context)
        {
            var tokens = _antiforgery.GetAndStoreTokens(context);
            context.Response.Cookies.Append(CookieName,
                tokens.RequestToken!,
                new CookieOptions()
                {
                    HttpOnly = false,
                    //SameSite = SameSiteMode.Strict
                });
            await _next.Invoke(context);
        }
    }
  5. Make AJAX request: This approach helps in situations where forms aren't directly used, like when working with APIs or SPAs (Single Page Applications).
    let token = Cookies.get('X-CSRF-TOKEN');       
     $.ajax({
         url: '/card',
         type: 'post',
         data: {
             id: "101",
         },
         headers: {
             "X-CSRF-TOKEN": token
         },           
         success: function (data, status) {
             alert("Status: " + status);
         },
         error: function (data, status) {
             alert("Status: " + status);
         },
     });

Output

Output

Summary

  • CSRF attacks trick authenticated users into making requests that they did not intend.
  • ASP.NET Core protects against CSRF by using anti-forgery tokens, which must be validated on the server for every POST request.
  • For AJAX requests, the token needs to be included in the request headers.

You can access the sample project here GitHub