liveBook · Manning

Anti-forgery token

As explained in Pro ASP.NET Core 7, Tenth Edition

An anti-forgery token is a server-generated value submitted with a request to verify that it originated from the legitimate application context.

An anti-forgery token is a server-generated value submitted with a request to verify that it originated from the legitimate application context. It counters cross-site request forgery (CSRF), where a malicious site causes a browser to send an authenticated request carrying the victim’s session cookie. section 27.8

ASP.NET Core’s FormTagHelper automatically adds a hidden token when a form uses routing attributes such as asp-controller, asp-action, or asp-page without an action attribute. Controllers must still enable validation. section 27.8 section 27.8.1

Configure validation according to the request surface:

  • Apply AutoValidateAntiforgeryToken to validate every HTTP method except GET, HEAD, OPTIONS, and TRACE. section 27.8.1
  • Use IgnoreValidationToken for an action or controller that must bypass validation, or ValidateAntiForgeryToken to enforce it, including on normally exempt methods. section 27.8.1
  • Test failure by removing or changing the hidden input through browser developer tools, then submitting the form. section 27.8.1

JavaScript client

For an HTML-free JavaScript client, expose the token through a readable XSRF-TOKEN cookie and accept it through the X-XSRF-TOKEN header. Client code reads the cookie and sends that value with its POST request. section 27.8.3

Listing 27.29 Enabling the anti-forgery feature in the Controllers/FormController.cs file
using Microsoft.AspNetCore.Mvc;
using WebApp.Models;
using Microsoft.EntityFrameworkCore;
using Microsoft.AspNetCore.Mvc.Rendering;

namespace WebApp.Controllers {

    [AutoValidateAntiforgeryToken]
    public class FormController : Controller {
        private DataContext context;
                
        public FormController(DataContext dbContext) {
            context = dbContext;
        }
                
        public async Task<IActionResult> Index(long id = 1) {
            ViewBag.Categories = new SelectList(context.Categories, 
                "CategoryId", "Name");
            return View("Form", await context.Products
                .Include(p => p.Category)
                .Include(p => p.Supplier)
                .FirstAsync(p => p.ProductId == id)
            ?? new() { Name = string.Empty });
        }
                
        [HttpPost]
        public IActionResult SubmitForm() {
            foreach (string key in Request.Form.Keys) {
                TempData[key] = string.Join(", ",
                    (string?)Request.Form[key]);
            }
            return RedirectToAction(nameof(Results));
        }
                
        public IActionResult Results() {
            return View();
        }
    }
}

Browse the controller example that applies AutoValidateAntiforgeryToken. section 27.8.1

Listing 27.31 Configuring the anti-forgery token in the WebApp/Program.cs file
using Microsoft.EntityFrameworkCore;
using WebApp.Models;
using Microsoft.AspNetCore.Antiforgery;

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddDbContext<DataContext>(opts => {
    opts.UseSqlServer(builder.Configuration[
        "ConnectionStrings:ProductConnection"]);
    opts.EnableSensitiveDataLogging(true);
});
builder.Services.AddControllersWithViews();
builder.Services.AddRazorPages();
builder.Services.AddSingleton<CitiesData>();

builder.Services.Configure<AntiforgeryOptions>(opts => {
    opts.HeaderName = "X-XSRF-TOKEN";
});

var app = builder.Build();

app.UseStaticFiles();

IAntiforgery antiforgery 
    = app.Services.GetRequiredService<IAntiforgery>();
app.Use(async (context, next) => {
    if (!context.Request.Path.StartsWithSegments("/api")) {
        string? token = 
            antiforgery.GetAndStoreTokens(context).RequestToken;
        if (token != null) {
            context.Response.Cookies.Append("XSRF-TOKEN",
               token,
               new CookieOptions { HttpOnly = false });
        }
    }
    await next();
});

app.MapControllerRoute("forms",
    "controllers/{controller=Home}/{action=Index}/{id?}");
app.MapRazorPages();

var context = app.Services.CreateScope().ServiceProvider
    .GetRequiredService<DataContext>();
SeedData.SeedDatabase(context);

app.Run();

Browse the middleware and AntiforgeryOptions configuration for cookie-and-header clients. section 27.8.3

Listing 27.32 The contents of the JavaScriptForm.cshtml file in the Pages folder
@page "/pages/jsform"

<script type="text/javascript">
   async function sendRequest() {
       const token = document.cookie.replace(
          /(?:(?:^|.*;\s*)XSRF-TOKEN\s*\=\s*([^;]*).*$)|^.*$/, "$1");
                  
       let form = new FormData();
       form.append("name", "Paddle");
       form.append("price", 100);
       form.append("categoryId", 1);
       form.append("supplierId", 1);
               
       let response = await fetch("@Url.Page("FormHandler")", {
           method: "POST",
           headers: { "X-XSRF-TOKEN": token },
           body: form
       });
       document.getElementById("content").innerHTML 
           = await response.text();
   }
       
   document.addEventListener("DOMContentLoaded",
       () => document.getElementById("submit").onclick = sendRequest);
</script>

<button class="btn btn-primary m-2" id="submit">
   Submit JavaScript Form
</button>
<div id="content"></div>

Browse the JavaScript fetch example that reads the cookie and sends the token header. section 27.8.3

How Pro ASP.NET Core 7, Tenth Edition covers it

How deep it goes
Explains CSRF, automatic form tokens, controller attributes, tamper testing, and cookie-header configuration without detailing token internals.
Examples
Uses FormController, hidden form fields, browser developer tools, XSRF-TOKEN cookies, X-XSRF-TOKEN headers, and fetch POST requests.
What it assumes
Assumes familiarity with ASP.NET Core controllers, forms, cookies, and JavaScript requests while explaining CSRF from its browser-request behavior.
What you can do after
Operate/configure anti-forgery validation for MVC forms, controller actions, and JavaScript clients.
How it connects
Connects anti-forgery tokens with CSRF, cookies, HTML forms, FormTagHelper, HTTP methods, middleware, and JavaScript clients.

Related concepts

This explainer was generated by Manning's knowledge graph from the text of Pro ASP.NET Core 7, Tenth Edition. The section links point to the parts of the book it draws on.