From 8cfe83edee1e6bf28a12ad553bc9a459e592bdfc Mon Sep 17 00:00:00 2001 From: Salih Date: Mon, 29 May 2023 12:38:49 +0300 Subject: [PATCH 1/5] Prevent breaking change --- .../Security/AbpSecurityHeadersMiddleware.cs | 27 +++++++------------ .../Security/AbpSecurityHeadersOptions.cs | 5 ++-- 2 files changed, 12 insertions(+), 20 deletions(-) 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 5e20edff17..c9b0738619 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 @@ -101,35 +101,28 @@ public class AbpSecurityHeadersMiddleware : IMiddleware, ITransientDependency protected virtual string BuildContentSecurityPolicyValue(HttpContext context) { + var cspValue = Options.Value.ContentSecurityPolicyValue.IsNullOrWhiteSpace() ? DefaultValue : Options.Value.ContentSecurityPolicyValue; if (!(Options.Value.UseContentSecurityPolicyScriptNonce && context.Items.TryGetValue(AbpAspNetCoreConsts.ScriptNonceKey, out var nonce) && nonce is string nonceValue && !string.IsNullOrEmpty(nonceValue))) { - return ContentSecurityPolicyValuesToCSPString(); + return cspValue; } - var scriptSrcValue = ""; - if (Options.Value.ContentSecurityPolicyValues.TryGetValue(ScriptSrcKey, out var scriptSrc)) - { - scriptSrcValue = string.Join(" ", scriptSrc); - } - - scriptSrcValue += $" 'nonce-{nonceValue}'"; + var nonceStr = $" 'nonce-{nonceValue}'"; - return ContentSecurityPolicyValuesToCSPString(true) + $"; {ScriptSrcKey} {scriptSrcValue}"; - } + var scriptSrcValue = Options.Value.ContentSecurityPolicyValue.Split(';') + .FirstOrDefault(x => x.Trim().StartsWith(ScriptSrcKey))?.Trim(); - protected virtual string ContentSecurityPolicyValuesToCSPString(bool ignoreScriptSrc = false) - { - if (Options.Value.ContentSecurityPolicyValues.Any()) + if (scriptSrcValue.IsNullOrWhiteSpace()) { - return string.Join("; ", - Options.Value.ContentSecurityPolicyValues.WhereIf(ignoreScriptSrc, x => x.Key != ScriptSrcKey) - .Select(x => $"{x.Key} {string.Join(" ", x.Value)}")); + return cspValue.EnsureEndsWith(';') + $" {ScriptSrcKey}{nonceStr};"; } - return DefaultValue; + var newScriptSrcValue = scriptSrcValue + nonceStr; + return Options.Value.ContentSecurityPolicyValue.Replace(scriptSrcValue, newScriptSrcValue); } + protected virtual void AddHeader(HttpContext context, string key, string value, bool overrideIfExists = false) { 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 index 427aa9ad9a..9a0bb5cd82 100644 --- a/framework/src/Volo.Abp.AspNetCore/Volo/Abp/AspNetCore/Security/AbpSecurityHeadersOptions.cs +++ b/framework/src/Volo.Abp.AspNetCore/Volo/Abp/AspNetCore/Security/AbpSecurityHeadersOptions.cs @@ -10,8 +10,8 @@ public class AbpSecurityHeadersOptions public bool UseContentSecurityPolicyHeader { get; set; } public bool UseContentSecurityPolicyScriptNonce { get; set; } - - public Dictionary> ContentSecurityPolicyValues { get; } + + public string ContentSecurityPolicyValue { get; set; } public Dictionary Headers { get; } @@ -22,7 +22,6 @@ public class AbpSecurityHeadersOptions public AbpSecurityHeadersOptions() { Headers = new Dictionary(); - ContentSecurityPolicyValues = new Dictionary>(); IgnoredScriptNonceSelectors = new List>>(); IgnoredScriptNoncePaths = new List(); } From 158e67fd893d76f7106a13cce2bad314d2730f7d Mon Sep 17 00:00:00 2001 From: Salih Date: Mon, 29 May 2023 15:14:10 +0300 Subject: [PATCH 2/5] Update Security-Headers doc --- docs/en/UI/AspNetCore/Security-Headers.md | 48 ++++++++++++++--------- 1 file changed, 29 insertions(+), 19 deletions(-) diff --git a/docs/en/UI/AspNetCore/Security-Headers.md b/docs/en/UI/AspNetCore/Security-Headers.md index f683a9c332..ebf17d3233 100644 --- a/docs/en/UI/AspNetCore/Security-Headers.md +++ b/docs/en/UI/AspNetCore/Security-Headers.md @@ -19,31 +19,13 @@ ABP Framework allows you to add frequently used security headers into your appli Configure(options => { options.UseContentSecurityPolicyHeader = true; //false by default - options.ContentSecurityPolicyValues["object-src"] = new string[] { "'none'" }; - options.ContentSecurityPolicyValues["form-action"] = new string[] { "'self'" }; - options.ContentSecurityPolicyValues["frame-ancestors"] = new string[] { "'self'" }; - options.ContentSecurityPolicyValues["script-src"] = new string[] { "'self'", "'unsafe-inline'", "'unsafe-eval'" }; - - //adding script-src nonce - options.UseContentSecurityPolicyScriptNonce = true; //false by default - - //ignore script nonce source for these paths - options.IgnoredScriptNoncePaths.Add("/my-page"); - - //ignore script nonce by Elsa Workflows and other selectors - options.IgnoredScriptNonceSelectors.Add(context => - { - var endpoint = context.GetEndpoint(); - return Task.FromResult(endpoint?.Metadata.GetMetadata()?.RouteTemplate == "/{YOURHOSTPAGE}"); - }); + options.ContentSecurityPolicyValue = "object-src 'none'; form-action 'self'; frame-ancestors 'none'"; //default value //adding additional security headers options.Headers["Referrer-Policy"] = "no-referrer"; }); ``` -> Using the script nonce feature will automatically add the nonce value to your script tags. There is no need to add it manually. However, if you still need to add it manually, you can use 'Html.GetScriptNonce()' to add the nonce value or 'Html.GetScriptNonceAttribute()' to add the nonce attribute value. - > If the header is the same, the additional security headers you defined take precedence over the default security headers. In other words, it overrides the default security headers' values. ## Security Headers Middleware @@ -61,3 +43,31 @@ app.UseAbpSecurityHeaders(); After that, you have registered the `UseAbpSecurityHeaders` middleware into the request pipeline, the defined security headers will be shown in the response headers as in the figure below: ![](../../images/security-response-headers.png) + +## Content Security Policy Script Nonce + +Abp Framework provides a property to add a dynamic script-src nonce value to the Content-Security-Policy header. With this feature, it automatically adds a dynamic nonce value to the header side. And with the help of the script tag helper, it adds this [`script nonce`](https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/nonce) value to the script tags on your pages(The `ScriptNonceTagHelper` in the `Volo.Abp.AspNetCore.Mvc.UI.Bundling` namespace must be attached as a taghelper.). + +This feature is disabled by default. You can enable it by setting the `UseContentSecurityPolicyScriptNonce` property of the `AbpSecurityHeadersOptions` class to `true`. + +**Example:** + +```csharp +Configure(options => +{ + //adding script-src nonce + options.UseContentSecurityPolicyScriptNonce = true; //false by default + + //ignore script nonce source for these paths + options.IgnoredScriptNoncePaths.Add("/my-page"); + + //ignore script nonce by Elsa Workflows and other selectors + options.IgnoredScriptNonceSelectors.Add(context => + { + var endpoint = context.GetEndpoint(); + return Task.FromResult(endpoint?.Metadata.GetMetadata()?.RouteTemplate == "/{YOURHOSTPAGE}"); + }); +}); +``` + +> If you need to add the nonce script manually, you can use 'Html.GetScriptNonce()' to add the nonce value or 'Html.GetScriptNonceAttribute()' to add the nonce attribute value. From b9a8be59e0d60a547666336ea1a91108da292d38 Mon Sep 17 00:00:00 2001 From: Salih Date: Mon, 29 May 2023 15:21:46 +0300 Subject: [PATCH 3/5] Update Security-Headers.md --- docs/en/UI/AspNetCore/Security-Headers.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/docs/en/UI/AspNetCore/Security-Headers.md b/docs/en/UI/AspNetCore/Security-Headers.md index ebf17d3233..c0d0dcc467 100644 --- a/docs/en/UI/AspNetCore/Security-Headers.md +++ b/docs/en/UI/AspNetCore/Security-Headers.md @@ -47,6 +47,7 @@ After that, you have registered the `UseAbpSecurityHeaders` middleware into the ## Content Security Policy Script Nonce Abp Framework provides a property to add a dynamic script-src nonce value to the Content-Security-Policy header. With this feature, it automatically adds a dynamic nonce value to the header side. And with the help of the script tag helper, it adds this [`script nonce`](https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/nonce) value to the script tags on your pages(The `ScriptNonceTagHelper` in the `Volo.Abp.AspNetCore.Mvc.UI.Bundling` namespace must be attached as a taghelper.). +> If you need to add the nonce script manually, you can use 'Html.GetScriptNonce()' to add the nonce value or 'Html.GetScriptNonceAttribute()' to add the nonce attribute value. This feature is disabled by default. You can enable it by setting the `UseContentSecurityPolicyScriptNonce` property of the `AbpSecurityHeadersOptions` class to `true`. @@ -69,5 +70,3 @@ Configure(options => }); }); ``` - -> If you need to add the nonce script manually, you can use 'Html.GetScriptNonce()' to add the nonce value or 'Html.GetScriptNonceAttribute()' to add the nonce attribute value. From fd3306e42c4411b476082cdceb86ecc8c1c27a89 Mon Sep 17 00:00:00 2001 From: Engincan VESKE <43685404+EngincanV@users.noreply.github.com> Date: Mon, 29 May 2023 15:28:33 +0300 Subject: [PATCH 4/5] Update SecurityHeadersTestController_Tests.cs --- .../Mvc/Security/Headers/SecurityHeadersTestController_Tests.cs | 1 - 1 file changed, 1 deletion(-) 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 7c704905e9..4c419ef744 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 @@ -29,7 +29,6 @@ public class SecurityHeadersTestController_Tests : AspNetCoreMvcTestBase 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'"); } [Fact] From d840d5c64f430051184fc9b788c5680f8246cfe8 Mon Sep 17 00:00:00 2001 From: Salih Date: Mon, 29 May 2023 15:35:06 +0300 Subject: [PATCH 5/5] Update Security-Headers.md --- docs/en/UI/AspNetCore/Security-Headers.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/docs/en/UI/AspNetCore/Security-Headers.md b/docs/en/UI/AspNetCore/Security-Headers.md index c0d0dcc467..b02e9691b0 100644 --- a/docs/en/UI/AspNetCore/Security-Headers.md +++ b/docs/en/UI/AspNetCore/Security-Headers.md @@ -51,6 +51,10 @@ Abp Framework provides a property to add a dynamic script-src nonce value to the This feature is disabled by default. You can enable it by setting the `UseContentSecurityPolicyScriptNonce` property of the `AbpSecurityHeadersOptions` class to `true`. +### Ignore Script Nonce + +You can ignore the script nonce for some pages or some selectors. You can use the `IgnoredScriptNoncePaths` and `IgnoredScriptNonceSelectors` properties of the `AbpSecurityHeadersOptions` class. + **Example:** ```csharp