|
| 1 | + |
| 2 | + |
1 | 3 | using System; |
2 | 4 | using System.Collections.Generic; |
3 | 5 | using System.IO; |
|
12 | 14 | using GxClasses.Web.Middleware; |
13 | 15 | using log4net; |
14 | 16 | using Microsoft.AspNetCore; |
| 17 | +using Microsoft.AspNetCore.Antiforgery; |
15 | 18 | using Microsoft.AspNetCore.Builder; |
16 | 19 | using Microsoft.AspNetCore.DataProtection; |
17 | 20 | using Microsoft.AspNetCore.Diagnostics; |
@@ -104,6 +107,10 @@ private static void LocatePhysicalLocalPath() |
104 | 107 |
|
105 | 108 | public static class GXHandlerExtensions |
106 | 109 | { |
| 110 | + public static IApplicationBuilder UseAntiforgeryTokens(this IApplicationBuilder app, string basePath) |
| 111 | + { |
| 112 | + return app.UseMiddleware<ValidateAntiForgeryTokenMiddleware>(basePath); |
| 113 | + } |
107 | 114 | public static IApplicationBuilder UseGXHandlerFactory(this IApplicationBuilder builder, string basePath) |
108 | 115 | { |
109 | 116 | return builder.UseMiddleware<HandlerFactory>(basePath); |
@@ -223,6 +230,13 @@ public void ConfigureServices(IServiceCollection services) |
223 | 230 | options.EnableForHttps = true; |
224 | 231 | }); |
225 | 232 | } |
| 233 | + if (RestAPIHelpers.ValidateCsrfToken()) |
| 234 | + { |
| 235 | + services.AddAntiforgery(options => |
| 236 | + { |
| 237 | + options.HeaderName = HttpHeader.X_GXCSRF_TOKEN; |
| 238 | + }); |
| 239 | + } |
226 | 240 | DefineCorsPolicy(services); |
227 | 241 | services.AddMvc(); |
228 | 242 | } |
@@ -386,6 +400,24 @@ public void Configure(IApplicationBuilder app, Microsoft.AspNetCore.Hosting.IHos |
386 | 400 |
|
387 | 401 | string restBasePath = string.IsNullOrEmpty(VirtualPath) ? REST_BASE_URL : $"{VirtualPath}/{REST_BASE_URL}"; |
388 | 402 | string apiBasePath = string.IsNullOrEmpty(VirtualPath) ? string.Empty : $"{VirtualPath}/"; |
| 403 | + if (RestAPIHelpers.ValidateCsrfToken()) |
| 404 | + { |
| 405 | + |
| 406 | + var antiforgery = app.ApplicationServices.GetRequiredService<IAntiforgery>(); |
| 407 | + app.UseAntiforgeryTokens(restBasePath); |
| 408 | + app.Run(async context => |
| 409 | + { |
| 410 | + string requestPath = context.Request.Path.Value; |
| 411 | + |
| 412 | + if (string.Equals(requestPath, $"/{restBasePath}VerificationToken", StringComparison.OrdinalIgnoreCase)) |
| 413 | + { |
| 414 | + var tokenSet = antiforgery.GetAndStoreTokens(context); |
| 415 | + context.Response.Cookies.Append(HttpHeader.X_GXCSRF_TOKEN, tokenSet.RequestToken!, |
| 416 | + new CookieOptions { HttpOnly = false }); |
| 417 | + } |
| 418 | + await Task.CompletedTask; |
| 419 | + }); |
| 420 | + } |
389 | 421 | app.UseMvc(routes => |
390 | 422 | { |
391 | 423 | foreach (string serviceBasePath in servicesBase) |
@@ -485,6 +517,13 @@ public async Task Invoke(HttpContext httpContext) |
485 | 517 | { |
486 | 518 | httpStatusCode = HttpStatusCode.NotFound; |
487 | 519 | } |
| 520 | + |
| 521 | + else if (ex is AntiforgeryValidationException) |
| 522 | + { |
| 523 | + //"The required antiforgery header value "X-GXCSRF-TOKEN" is not present. |
| 524 | + httpStatusCode = HttpStatusCode.BadRequest; |
| 525 | + GXLogging.Error(log, $"Validation of antiforgery failed", ex); |
| 526 | + } |
488 | 527 | else |
489 | 528 | { |
490 | 529 | httpStatusCode = HttpStatusCode.InternalServerError; |
@@ -542,4 +581,43 @@ public IActionResult Index() |
542 | 581 | return Redirect(defaultFiles[0]); |
543 | 582 | } |
544 | 583 | } |
| 584 | + public class ValidateAntiForgeryTokenMiddleware |
| 585 | + { |
| 586 | + static readonly ILog log = log4net.LogManager.GetLogger(typeof(ValidateAntiForgeryTokenMiddleware)); |
| 587 | + |
| 588 | + private readonly RequestDelegate _next; |
| 589 | + private readonly IAntiforgery _antiforgery; |
| 590 | + private string _basePath; |
| 591 | + |
| 592 | + public ValidateAntiForgeryTokenMiddleware(RequestDelegate next, IAntiforgery antiforgery, String basePath) |
| 593 | + { |
| 594 | + _next = next; |
| 595 | + _antiforgery = antiforgery; |
| 596 | + _basePath = "/" + basePath; |
| 597 | + } |
| 598 | + |
| 599 | + public async Task Invoke(HttpContext context) |
| 600 | + { |
| 601 | + if (context.Request.Path.HasValue && context.Request.Path.Value.StartsWith(_basePath) && HttpMethods.IsPost(context.Request.Method)) |
| 602 | + { |
| 603 | + GXLogging.Debug(log, $"Antiforgery validation starts"); |
| 604 | + await _antiforgery.ValidateRequestAsync(context); |
| 605 | + GXLogging.Debug(log, $"Antiforgery validation OK"); |
| 606 | + } |
| 607 | + else if (HttpMethods.IsGet(context.Request.Method)) |
| 608 | + { |
| 609 | + string tokens = context.Request.Cookies[HttpHeader.X_GXCSRF_TOKEN]; |
| 610 | + if (string.IsNullOrEmpty(tokens)) |
| 611 | + { |
| 612 | + GXLogging.Debug(log, $"Setting cookie ", HttpHeader.X_GXCSRF_TOKEN); |
| 613 | + var tokenSet = _antiforgery.GetAndStoreTokens(context); |
| 614 | + context.Response.Cookies.Append(HttpHeader.X_GXCSRF_TOKEN, tokenSet.RequestToken!, |
| 615 | + new CookieOptions { HttpOnly = false }); |
| 616 | + } |
| 617 | + } |
| 618 | + |
| 619 | + await _next(context); |
| 620 | + } |
| 621 | + |
| 622 | + } |
545 | 623 | } |
0 commit comments