ASP.NET Core MVC Mastery

Anti-forgery

1 Views Updated 5/4/2026

Anti-Forgery (CSRF) Protection — Securing Data Mutation

Cross-Site Request Forgery (CSRF) is a devastating attack where a malicious website forces an authenticated user's browser to execute unwanted actions (like transferring funds or changing passwords) on a trusted site. ASP.NET Core MVC deeply integrates the Anti-Forgery Token pattern to decisively block these attacks.

1. WHAT is CSRF and How Do Tokens Work?

A CSRF attack relies on a browser's automated behavior: if you are logged into bank.com, your browser stores a session cookie. If you visit hacker.com, the hacker can create an invisible form pointing to bank.com/transfer. When hacker.com submits that form, your browser automatically attaches the bank.com session cookie. The server thinks you authorized the transfer.

The Synchronizer Token Pattern
  1. When rendering a form, ASP.NET generates a unique, cryptographically secure Token.
  2. It places this token in a hidden HTML <input> field AND in an encrypted HttpOnly Cookie.
  3. When the form is POSTed back, ASP.NET compares the Token in the form data to the Token in the cookie.
  4. The Hacker's Failing: The hacker on hacker.com cannot read the bank's cookie (due to Same-Origin Policy) and therefore cannot know the correct token to put in their fake form. The server blocks the request with a 400 Bad Request.

2. REAL-TIME PRODUCTION EXAMPLES

Example 1: Standard Razor View Forms

In modern ASP.NET Core, if you use the Form Tag Helper (<form asp-action="...">), the Anti-Forgery token is injected automatically. You do nothing.

<!-- The Tag Helper automatically generates the hidden token input -->
<form asp-controller="Transfer" asp-action="Execute" method="post">
    <input type="text" name="amount" />
    <button type="submit">Transfer</button>
</form>

<!-- Renders as: -->
<form action="/Transfer/Execute" method="post">
    <input name="amount" type="text" />
    <button type="submit">Transfer</button>
    <input name="__RequestVerificationToken" type="hidden" value="CfDJ8J..." />
</form>
// The Controller
public class TransferController : Controller
{
    [HttpPost]
    [ValidateAntiForgeryToken] // CRITICAL: This enforces validation
    public IActionResult Execute(decimal amount)
    {
        // Safe from CSRF!
        return Ok();
    }
}

Example 2: AutoValidateAntiforgeryToken (Enterprise Default)

Relying on developers to remember [ValidateAntiForgeryToken] on every POST/PUT/DELETE is a recipe for disaster. Apply it globally.

// Program.cs (.NET 6+)
builder.Services.AddControllersWithViews(options =>
{
    // Automatically validates tokens for ALL mutable requests (POST, PUT, DELETE, PATCH)
    // Ignores safe GET, HEAD, TRACE, OPTIONS requests.
    options.Filters.Add(new AutoValidateAntiforgeryTokenAttribute());
});

Now you explicitly opt-OUT via [IgnoreAntiforgeryToken] only for external webhooks or public APIs that use JWTs instead of cookies.

Example 3: AJAX & Single Page Applications (React/Vue/jQuery)

If you POST data via JavaScript fetch() or jQuery rather than standard HTML forms, the hidden input isn't automatically sent. You must extract it and send it as an HTTP Header.

<!-- Ensure the token is on the page -->
@Html.AntiForgeryToken()

<script>
    async function doSecurePostData() {
        // Read the token from the hidden input generated above
        const token = document.querySelector('input[name="__RequestVerificationToken"]').value;

        await fetch('/api/secure-action', {
            method: 'POST',
            headers: {
                'Content-Type': 'application/json',
                // ASP.NET Core automatically looks for this specific header name for AJAX
                'RequestVerificationToken': token
            },
            body: JSON.stringify({ amount: 100 })
        });
    }
</script>

3. The IAntiforgery API (Advanced SPAs)

For strictly separated SPAs (Angular/React) that don't use Razor Views, you can generate and send the token to the client via an endpoint or a cookie.

public class AuthController : Controller
{
    private readonly IAntiforgery _antiforgery;
    public AuthController(IAntiforgery antiforgery) => _antiforgery = antiforgery;

    [HttpGet("api/get-csrf-token")]
    public IActionResult GetToken()
    {
        // Generate the token set
        var tokens = _antiforgery.GetAndStoreTokens(HttpContext);

        // Tell the SPA what the token is, so it can include it in future POST headers
        return Ok(new { token = tokens.RequestToken });
    }
}

4. Best Practices

✅ DO
  • Apply AutoValidateAntiforgeryTokenAttribute globally in Program.cs.
  • Ensure all state-mutating actions (create, edit, delete, fund transfers) are restricted to POST/PUT/DELETE.
  • Rely on Tag Helpers for forms—they handle token injection automatically.
❌ DON'T
  • Never use HTTP GET to mutate state (e.g., /Home/DeleteUser/5). Anti-forgery tokens only protect POST/PUT/DELETE requests. A GET link can be maliciously embedded in an image tag (<img src="/Home/DeleteUser/5">), executing immediately.
  • Avoid disabling tokens globally just because an API is failing. Instead, use [IgnoreAntiforgeryToken] solely on the endpoints acting as public webhooks.

5. Interview Mastery

Q: "If my application only uses JWT (JSON Web Tokens) for authentication, and stores them in LocalStorage instead of Cookies, do I still need Anti-Forgery Tokens?"

Architect Answer: "No. CSRF attacks strictly rely on the browser's automated behavior of blindly attaching Cookies to every out-bound request to that domain. If your app uses LocalStorage and JavaScript explicitly attaches an Authorization: Bearer <JWT> header to API calls, a hacker's forged form cannot access your LocalStorage, so it cannot attach the JWT. However, storing JWTs in LocalStorage opens you up to XSS (Cross-Site Scripting). For maximum enterprise security, the current best practice is the 'BFF' (Backend For Frontend) pattern: store JWTs in secure, server-side HttpOnly cookies, and thus, re-introduce Anti-Forgery tokens to protect those cookies."

ASP.NET Core MVC Mastery
1. Core Framework
Introduction to ASP.NET Core MVC
MODULE 1: INTRODUCTION & ENVIRONMENT SETUP
Microsoft Web Stack Overview Evolution of ASP.NET Environment Setup
2. View Engine
Layouts & Partial Views in Razor
MODULE 2: .NET CORE FUNDAMENTALS
Core Concepts Project Structure Startup Flow Middleware Pipeline
MODULE 3: ASP.NET CORE BASICS
Creating Project CLI Commands wwwroot & Static Files
MODULE 4: MVC FUNDAMENTALS
MVC Architecture Dependency Injection (DI) Service Lifetimes
MODULE 5: DATA PASSING TECHNIQUES
ViewData vs ViewBag TempData ViewModel Pattern
MODULE 6: ROUTING
Conventional vs Attribute Routing Custom Constraints
MODULE 7: VIEWS & UI
Razor View Engine Layouts & Sections View Components
MODULE 8: ACTION RESULTS
ViewResult JsonResult RedirectResult
MODULE 9: HTML HELPERS
Form Helpers Custom HTML Helpers
MODULE 10: TAG HELPERS
Built-in Tag Helpers Custom Tag Helpers
MODULE 11: MODEL BINDING
FromQuery vs FromRoute Complex Binding
MODULE 12: VALIDATION
Data Annotations Remote Validation Fluent Validation
MODULE 13: STATE MANAGEMENT
Cookies & Sessions TempData
MODULE 14: FILTERS & SECURITY
Action Filters Authorize Filters Anti-forgery
MODULE 15: ENTITY FRAMEWORK CORE (DEEP DIVE)
DbContext Migrations LINQ Relationships
MODULE 16: DESIGN PATTERNS
Repository Pattern Unit of Work Clean Architecture
MODULE 17: FILE HANDLING
File Upload/Download PDF/Excel Generation
MODULE 18: ADVANCED ASP.NET CORE
Request Lifecycle Bundling & Minification Deployment
MODULE 19: PERFORMANCE & BEST PRACTICES
Caching Strategies Async Programming Secure Coding
MODULE 20: RAZOR PAGES (BONUS)
Razor Pages vs MVC
MODULE 21: REAL-WORLD PROJECTS (🔥 MUST DO)
E-Commerce Web Application Employee Management System