admin管理员组

文章数量:1122846

I want to customize the response received by the client when CSRF token validation fails. I was looking at ASP.NET's source code and found a middleware I think handles that.

By looking at it, I saw that it sets an IAntiForgeryValidationFeature with an error when validation fails, so I created a middleware to check that.

public class CustomAntiForgeryValidationResponseMiddleware(RequestDelegate next)
{
    public async Task InvokeAsync(HttpContext context)
    {
        IAntiforgeryValidationFeature? antiforgeryValidation = context.Features.Get<IAntiforgeryValidationFeature>();

        if (antiforgeryValidation is not null && !antiforgeryValidation.IsValid)
        {
            context.Response.StatusCode = 403;
            await context.Response.WriteAsJsonAsync(new ProblemDetails()
            {
                Title = "Forbidden",
                Detail = "User is not allowed to perform this action",
                Status = 403,
                Extensions = new Dictionary<string, object?>()
                {
                    { "errors", new ApiError[] { new("Invalid or missing CSRF token") } }
                }
            });
        }

        await next(context);
    }
}

I then added the middleware before the authentication one in the request pipeline.

app.UseMiddleware<CustomAntiForgeryValidationResponseMiddleware>();

app.UseAuthentication();
app.UseAuthorization();

app.MapControllers();

app.Run();

However the feature is always null even on endpoints that require a CSRF token. What am I doing wrong?

I want to customize the response received by the client when CSRF token validation fails. I was looking at ASP.NET's source code and found a middleware I think handles that.

By looking at it, I saw that it sets an IAntiForgeryValidationFeature with an error when validation fails, so I created a middleware to check that.

public class CustomAntiForgeryValidationResponseMiddleware(RequestDelegate next)
{
    public async Task InvokeAsync(HttpContext context)
    {
        IAntiforgeryValidationFeature? antiforgeryValidation = context.Features.Get<IAntiforgeryValidationFeature>();

        if (antiforgeryValidation is not null && !antiforgeryValidation.IsValid)
        {
            context.Response.StatusCode = 403;
            await context.Response.WriteAsJsonAsync(new ProblemDetails()
            {
                Title = "Forbidden",
                Detail = "User is not allowed to perform this action",
                Status = 403,
                Extensions = new Dictionary<string, object?>()
                {
                    { "errors", new ApiError[] { new("Invalid or missing CSRF token") } }
                }
            });
        }

        await next(context);
    }
}

I then added the middleware before the authentication one in the request pipeline.

app.UseMiddleware<CustomAntiForgeryValidationResponseMiddleware>();

app.UseAuthentication();
app.UseAuthorization();

app.MapControllers();

app.Run();

However the feature is always null even on endpoints that require a CSRF token. What am I doing wrong?

Share Improve this question asked Nov 21, 2024 at 12:02 iKingNinjaiKingNinja 3934 silver badges13 bronze badges
Add a comment  | 

2 Answers 2

Reset to default 1

I solved the issue by creating a custom implementation of AutoValidateAntiforgeryTokenAttribute which uses a custom implementation of ValidateAntiforgeryTokenAuthorizationFilter which calls IAntiforgery.ValidateRequestAsync() and writes the custom response body if it throws an exception.

It turned out that the antiforgery middleware was not running at all, but I'm not sure why (probably because the project was not a minimal API).

Generally, Anti-Forgery token validation failed will throw a 400 bad request error. Besides, if the request data invalid, it will also return a 400 bad request error.

So, before capturing the Anti-Forgery token validation exception, we need to know the Anti-Forgery Tokens workflow:

To help prevent CSRF attacks, ASP.NET MVC uses anti-forgery tokens, also called request verification tokens.

  1. The client requests an HTML page that contains a form. The server includes two tokens in the response. One token is sent as a cookie.
  2. The other is placed in a hidden form field. The tokens are generated randomly so that an adversary cannot guess the values. When the client submits the form, it must send both tokens back to the server.
  3. The client sends the cookie token as a cookie, and it sends the form token inside the form data. (A browser client automatically does this when the user submits the form.)
  4. If a request does not include both tokens, the server disallows the request.

So, you can try to use the UseStatusCodePages method to capture the 400 error and check whether the request header or form contains the Anti-Forgery token: if the request header or form doesn't contain the Anti-Forgery token, this is a Anti-Forgery token exception, code as below:

app.UseStatusCodePages(async context =>
{
    var response = context.HttpContext.Response;

    if (response.StatusCode == 400 )
    {
        //check Anti-Forgery Tokens workflow: https://learn.microsoft.com/en-us/aspnet/web-api/overview/security/preventing-cross-site-request-forgery-csrf-attacks#anti-forgery-tokens
        if (!context.HttpContext.Request.Headers.ContainsKey("RequestVerificationToken") & !context.HttpContext.Request.Form.ContainsKey("__RequestVerificationToken"))
        { 
            //Anti-Forgery Tokens exceptions
            response.StatusCode = StatusCodes.Status403Forbidden;
            await response.WriteAsync("Antiforgery token validation failed.");
        }
        else
        {
            //other model validation error
            await response.WriteAsync("Other 400 error.");
        }
    }
});

app.UseHttpsRedirection();
app.UseRouting();
app.UseAuthorization();

本文标签: cCustomize ASPNET Core39s AntiForgery failure responseStack Overflow