🛡️ Security in .NET: Preventing CSRF (Cross-Site Request Forgery)
Imagine a user is logged into your application.
They open a malicious "cat video" website in another tab.
That website contains a hidden form which silently sends a POST request to your API:
POST /api/account/delete
Because the user is authenticated, their browser automatically includes the session cookie.
Your API receives a valid request from an authenticated user and executes it.
The user never intended this action.
This is called Cross-Site Request Forgery (CSRF).
1️⃣ The Defense: Anti-Forgery Tokens
The most widely used protection mechanism is the Synchronizer Token Pattern.
The idea is simple:
- The server generates a unique secret token.
- The token is sent to the client.
- The client must include the token in every state-changing request.
- The server validates the token before processing the request.
Since a malicious website cannot read tokens from another domain, forged requests fail validation.
2️⃣ Professional Implementation in ASP.NET Core
ASP.NET Core provides built-in support for CSRF protection via the Antiforgery service.
This integrates seamlessly with Single Page Applications such as Angular or React.
Step A: Configure the Antiforgery Service
In Program.cs, configure the header name used by the frontend:
builder.Services.AddAntiforgery(options =>
{
options.HeaderName = "X-XSRF-TOKEN";
});
The frontend must send this header with every POST, PUT, PATCH, or DELETE request.
Step B: Automatically Validate Requests
Instead of decorating every controller action with attributes, we register a global filter.
builder.Services.AddControllers(options =>
{
options.Filters.Add(
new AutoValidateAntiforgeryTokenAttribute()
);
});
This automatically validates anti-forgery tokens for:
- POST requests
- PUT requests
- PATCH requests
- DELETE requests
Step C: Token Provider Middleware
Since APIs are typically stateless, the frontend needs a secure way to retrieve the token.
We create middleware that sends the token as a cookie.
app.Use((context, next) =>
{
var antiforgery =
context.RequestServices
.GetRequiredService<IAntiforgery>();
var tokens =
antiforgery.GetAndStoreTokens(context);
context.Response.Cookies.Append(
"XSRF-TOKEN",
tokens.RequestToken!,
new CookieOptions
{
HttpOnly = false,
Secure = true,
SameSite = SameSiteMode.Strict
});
return next(context);
});
3️⃣ Why This Protection Works
This implementation leverages multiple browser security mechanisms:
| Mechanism | Purpose |
|---|---|
| Same-Origin Policy | Prevents malicious sites from reading cookies |
| XSRF Token Cookie | Stores the secret token for JavaScript access |
| X-XSRF-TOKEN Header | Sends the token back to the server |
| Token Validation | Ensures request originates from trusted frontend |
🔍 Cookie Configuration Explained
| Setting | Reason |
|---|---|
HttpOnly = false |
Allows JavaScript frameworks to read the token |
Secure = true |
Ensures cookie is sent only over HTTPS |
SameSite = Strict |
Blocks cookie transmission from other origins |
4️⃣ Frontend Example (Axios Interceptor)
Frontend frameworks typically read the cookie and send the token automatically:
axios.interceptors.request.use(config => {
const token =
document.cookie
.split('; ')
.find(row => row.startsWith('XSRF-TOKEN'))
?.split('=')[1];
if (token) {
config.headers['X-XSRF-TOKEN'] = token;
}
return config;
});
📊 Security Coverage Summary
| Threat | Protection |
|---|---|
| XSS | Prevents script injection |
| CSP | Restricts resource execution |
| CSRF | Prevents unauthorized actions |
🏁 Final Thoughts
CSRF protection ensures that authenticated users only perform actions they explicitly intend.
Combined with:
- Input Sanitization
- Content Security Policy
- Secure Cookies
- Unified API Responses
your application achieves a strong defense-in-depth architecture.
Comments
Post a Comment