0
votes

I have to protect my Web Application against CSRF, which is a .Net core MVC web app with Angular 9 in the client side.

Here is what I have tried

// Startup.cs
public void ConfigureServices(IServiceCollection services)
{
  services.AddAntiforgery(options => options.HeaderName = "X-XSRF-TOKEN");
  services.AddMvc(option => option.EnableEndpointRouting = false);
}

 public void Configure(IApplicationBuilder app, IWebHostEnvironment env, IAntiforgery antiforgery)
{
app.Use((context, next) =>
            {
               
                // CSRF cookie token generation
                string path = context.Request.Path.Value;

                if (
                    string.Equals(path, "/", StringComparison.OrdinalIgnoreCase) ||
                    string.Equals(path, "/index.html", StringComparison.OrdinalIgnoreCase))
                {
                    // The request token can be sent as a JavaScript-readable cookie, 
                    // and Angular uses it by default.
                    var tokens = antiforgery.GetAndStoreTokens(context);
                    context.Response.Cookies.Append("XSRF-TOKEN", tokens.RequestToken,
                        new CookieOptions() { HttpOnly = false });
                }
                // CSRF cookie token generation - end
                return next.Invoke();
            });
}


It is generating XSRF-TOKEN cookie but the Angular is not setting X-XSRF-TOKEN header in the request. cookie I am not making any code changes in Angular request part.

Controller.cs

       [ValidateAntiForgeryToken]
        [HttpPost]
        public IActionResult ProduceMessage([FromBody] OncRequestData oncRequestData)
        {
            OncRequestData _OncRequestData = new OncRequestData();
      }

In Angular app I have added a httpintercepter to extract and send the token in the request header


export class TokenInterceptorService implements HttpInterceptor {
  token: string;

  constructor(private  xsrfTokenExtractor: HttpXsrfTokenExtractor) { }

  intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
  
    if(req.method == "POST")
    {   
    let xsrfToken = this.xsrfTokenExtractor.getToken();
    const authReq = req.clone({ headers: req.headers.set("X-XSRF-TOKEN", xsrfToken) });
    return next.handle(authReq);    
    
  }else{
    return next.handle(req);
  }
  }

Now I am getting a bad request error (400) from the server

1
Thanks @R.Richards , I feel I am doing the same thing as it mentioned , setting a cookie XSRF-TOKEN and expect the angular to send the token in X-XSRF-TOKEN header, which is not happening. - SijoX

1 Answers

0
votes

I fixed this issue temporarily by making below changes.

  1. I removed the [ValidateAntiForgeryToken] attribute from action mthod
  2. added a custom middleware to validate all the POST request
  3. Trigger the ValidateRequestAsync(httpContext); method to validate request.

 public class AntiforgeryValidationMiddleware
    {
        private readonly RequestDelegate _next;
        public AntiforgeryValidationMiddleware(RequestDelegate next)
        {
            _next = next;
        }
        /// <summary>
        /// Validate incoming request for CSRF token
        /// </summary>
        /// <param name="httpContext"></param>
        /// <param name="antiforgery"></param>
        /// <returns></returns>
        public async Task InvokeAsync(HttpContext httpContext, IAntiforgery antiforgery)
        {
            try
            {
                // if the POST request is not from AD2BC call back 
                if (httpContext.Request.Method == "POST" && !httpContext.Request.Path.Equals("/Home/Auth"))
                {
                    await antiforgery.ValidateRequestAsync(httpContext);
                }
                await _next(httpContext);
            }
            catch (AntiforgeryValidationException exception)
            {
                Log.Error("CSRF token validation failed" + exception.Message);
               
            }
        }
     }

Please let me know if anyone have a better soluton