diff --git a/framework/src/Volo.Abp.AspNetCore/Volo/Abp/AspNetCore/Security/AbpSecurityHeadersMiddleware.cs b/framework/src/Volo.Abp.AspNetCore/Volo/Abp/AspNetCore/Security/AbpSecurityHeadersMiddleware.cs index 583b12efcb..0245cf11bc 100644 --- a/framework/src/Volo.Abp.AspNetCore/Volo/Abp/AspNetCore/Security/AbpSecurityHeadersMiddleware.cs +++ b/framework/src/Volo.Abp.AspNetCore/Volo/Abp/AspNetCore/Security/AbpSecurityHeadersMiddleware.cs @@ -1,6 +1,8 @@ +using System; using System.Collections.Generic; using System.Threading.Tasks; using Microsoft.AspNetCore.Http; +using Microsoft.Extensions.Options; using Microsoft.Extensions.Primitives; using Volo.Abp.DependencyInjection; @@ -8,6 +10,13 @@ namespace Volo.Abp.AspNetCore.Security; public class AbpSecurityHeadersMiddleware : IMiddleware, ITransientDependency { + public IOptions Options { get; set; } + + public AbpSecurityHeadersMiddleware(IOptions options) + { + Options = options; + } + public async Task InvokeAsync(HttpContext context, RequestDelegate next) { /*X-Content-Type-Options header tells the browser to not try and “guess” what a mimetype of a resource might be, and to just take what mimetype the server has returned as fact.*/ @@ -22,6 +31,14 @@ public class AbpSecurityHeadersMiddleware : IMiddleware, ITransientDependency /*The X-Content-Type-Options response HTTP header is a marker used by the server to indicate that the MIME types advertised in the Content-Type headers should be followed and not be changed. The header allows you to avoid MIME type sniffing by saying that the MIME types are deliberately configured.*/ AddHeaderIfNotExists(context, "X-Content-Type-Options", "nosniff"); + if (Options.Value.UseContentSecurityPolicyHeader) + { + AddHeaderIfNotExists(context, "Content-Security-Policy", + Options.Value.ContentSecurityPolicyValue.IsNullOrEmpty() + ? "object-src 'none'; form-action 'self'; frame-ancestors 'none'" + : Options.Value.ContentSecurityPolicyValue); + } + await next.Invoke(context); } diff --git a/framework/src/Volo.Abp.AspNetCore/Volo/Abp/AspNetCore/Security/AbpSecurityHeadersOptions.cs b/framework/src/Volo.Abp.AspNetCore/Volo/Abp/AspNetCore/Security/AbpSecurityHeadersOptions.cs new file mode 100644 index 0000000000..198a62723c --- /dev/null +++ b/framework/src/Volo.Abp.AspNetCore/Volo/Abp/AspNetCore/Security/AbpSecurityHeadersOptions.cs @@ -0,0 +1,8 @@ +namespace Volo.Abp.AspNetCore.Security; + +public class AbpSecurityHeadersOptions +{ + public bool UseContentSecurityPolicyHeader { get; set; } + + public string ContentSecurityPolicyValue { get; set; } +} diff --git a/framework/test/Volo.Abp.AspNetCore.Mvc.Tests/Volo/Abp/AspNetCore/Mvc/Security/Headers/SecurityHeadersTestController_Tests.cs b/framework/test/Volo.Abp.AspNetCore.Mvc.Tests/Volo/Abp/AspNetCore/Mvc/Security/Headers/SecurityHeadersTestController_Tests.cs index 5a0f0b3395..fdfda83ac9 100644 --- a/framework/test/Volo.Abp.AspNetCore.Mvc.Tests/Volo/Abp/AspNetCore/Mvc/Security/Headers/SecurityHeadersTestController_Tests.cs +++ b/framework/test/Volo.Abp.AspNetCore.Mvc.Tests/Volo/Abp/AspNetCore/Mvc/Security/Headers/SecurityHeadersTestController_Tests.cs @@ -1,12 +1,25 @@ using System.Linq; using System.Threading.Tasks; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; using Shouldly; +using Volo.Abp.AspNetCore.Security; using Xunit; namespace Volo.Abp.AspNetCore.Mvc.Security.Headers; public class SecurityHeadersTestController_Tests : AspNetCoreMvcTestBase { + protected override void ConfigureServices(HostBuilderContext context, IServiceCollection services) + { + services.Configure(options => + { + options.UseContentSecurityPolicyHeader = true; + }); + + base.ConfigureServices(context, services); + } + [Fact] public async Task SecurityHeaders_Should_Be_Added() { @@ -14,5 +27,7 @@ public class SecurityHeadersTestController_Tests : AspNetCoreMvcTestBase responseMessage.Headers.ShouldContain(x => x.Key == "X-Content-Type-Options" & x.Value.First().ToString() == "nosniff"); responseMessage.Headers.ShouldContain(x => x.Key == "X-XSS-Protection" & x.Value.First().ToString() == "1; mode=block"); responseMessage.Headers.ShouldContain(x => x.Key == "X-Frame-Options" & x.Value.First().ToString() == "SAMEORIGIN"); + responseMessage.Headers.ShouldContain(x => x.Key == "X-Content-Type-Options" & x.Value.First().ToString() == "nosniff"); + responseMessage.Headers.ShouldContain(x => x.Key == "Content-Security-Policy" & x.Value.First().ToString() == "object-src 'none'; form-action 'self'; frame-ancestors 'none'"); } }