diff --git a/identity-server/src/IdentityServer/IdentityServerConstants.cs b/identity-server/src/IdentityServer/IdentityServerConstants.cs index 7a6a32859..947c01fc5 100644 --- a/identity-server/src/IdentityServer/IdentityServerConstants.cs +++ b/identity-server/src/IdentityServer/IdentityServerConstants.cs @@ -235,7 +235,7 @@ public static class ContentSecurityPolicyHashes /// /// The hash of the inline script used on the check session endpoint. /// - public const string CheckSessionScript = "sha256-fa5rxHhZ799izGRP38+h4ud5QXNT0SFaFlh4eqDumBI="; + public const string CheckSessionScript = "sha256-4Hj97GNFvt0k8A6DbSr2hoRb/RJmCCakAgE+4zuVeHs="; } public static class ProtocolRoutePaths diff --git a/identity-server/test/IdentityServer.UnitTests/Endpoints/Results/AuthorizeResultTests.cs b/identity-server/test/IdentityServer.UnitTests/Endpoints/Results/AuthorizeResultTests.cs index 3573dec02..14abd0e3a 100644 --- a/identity-server/test/IdentityServer.UnitTests/Endpoints/Results/AuthorizeResultTests.cs +++ b/identity-server/test/IdentityServer.UnitTests/Endpoints/Results/AuthorizeResultTests.cs @@ -2,6 +2,7 @@ // See LICENSE in the project root for license information. +using System.Text.RegularExpressions; using Duende.IdentityModel; using Duende.IdentityServer; using Duende.IdentityServer.Configuration; @@ -226,6 +227,32 @@ public async Task form_post_mode_should_pass_results_in_body() html.ShouldContain(""); } + [Fact] + public async Task csp_hash_should_match_inline_script() + { + _response.Request = new ValidatedAuthorizeRequest + { + ClientId = "client", + ResponseMode = OidcConstants.ResponseModes.FormPost, + RedirectUri = "http://client/callback", + State = "state" + }; + + await _subject.WriteHttpResponse(new AuthorizeResult(_response), _context); + + _context.Response.StatusCode.ShouldBe(200); + _context.Response.ContentType.ShouldStartWith("text/html"); + _context.Response.Body.Seek(0, SeekOrigin.Begin); + using var rdr = new StreamReader(_context.Response.Body); + var html = await rdr.ReadToEndAsync(); + + var match = Regex.Match(html, "", RegexOptions.Singleline | RegexOptions.IgnoreCase); + match.Success.ShouldBeTrue(); + + var scriptSha256 = "sha256-" + match.Groups[1].Value.ToSha256(); + IdentityServerConstants.ContentSecurityPolicyHashes.AuthorizeScript.ShouldContain(scriptSha256); + } + [Fact] public async Task form_post_mode_should_add_unsafe_inline_for_csp_level_1() { diff --git a/identity-server/test/IdentityServer.UnitTests/Endpoints/Results/CheckSessionResultTests.cs b/identity-server/test/IdentityServer.UnitTests/Endpoints/Results/CheckSessionResultTests.cs index 0d17795b6..975e6a707 100644 --- a/identity-server/test/IdentityServer.UnitTests/Endpoints/Results/CheckSessionResultTests.cs +++ b/identity-server/test/IdentityServer.UnitTests/Endpoints/Results/CheckSessionResultTests.cs @@ -2,6 +2,8 @@ // See LICENSE in the project root for license information. +using System.Text.RegularExpressions; +using Duende.IdentityModel; using Duende.IdentityServer; using Duende.IdentityServer.Configuration; using Duende.IdentityServer.Endpoints.Results; @@ -46,6 +48,24 @@ public async Task should_pass_results_in_body() html.ShouldContain(""); } + [Fact] + public async Task csp_hash_should_match_inline_script() + { + await _subject.WriteHttpResponse(new CheckSessionResult(), _context); + + _context.Response.StatusCode.ShouldBe(200); + _context.Response.ContentType.ShouldStartWith("text/html"); + _context.Response.Body.Seek(0, SeekOrigin.Begin); + using var rdr = new StreamReader(_context.Response.Body); + var html = await rdr.ReadToEndAsync(); + + var match = Regex.Match(html, "", RegexOptions.Singleline | RegexOptions.IgnoreCase); + match.Success.ShouldBeTrue(); + + var scriptSha256 = "sha256-" + match.Groups[1].Value.ToSha256(); + IdentityServerConstants.ContentSecurityPolicyHashes.CheckSessionScript.ShouldContain(scriptSha256); + } + [Fact] public async Task form_post_mode_should_add_unsafe_inline_for_csp_level_1() { diff --git a/identity-server/test/IdentityServer.UnitTests/Endpoints/Results/EndSessionCallbackResultTests.cs b/identity-server/test/IdentityServer.UnitTests/Endpoints/Results/EndSessionCallbackResultTests.cs index 74eb6204f..20dbad1e5 100644 --- a/identity-server/test/IdentityServer.UnitTests/Endpoints/Results/EndSessionCallbackResultTests.cs +++ b/identity-server/test/IdentityServer.UnitTests/Endpoints/Results/EndSessionCallbackResultTests.cs @@ -2,6 +2,9 @@ // See LICENSE in the project root for license information. +using System.Text.RegularExpressions; +using Duende.IdentityModel; +using Duende.IdentityServer; using Duende.IdentityServer.Configuration; using Duende.IdentityServer.Endpoints.Results; using Duende.IdentityServer.Models; @@ -52,10 +55,10 @@ public async Task success_should_render_html_and_iframes() _context.Response.Headers.CacheControl.First().ShouldContain("no-cache"); _context.Response.Headers.CacheControl.First().ShouldContain("max-age=0"); _context.Response.Headers.ContentSecurityPolicy.First().ShouldContain("default-src 'none';"); - _context.Response.Headers.ContentSecurityPolicy.First().ShouldContain("style-src 'sha256-e6FQZewefmod2S/5T11pTXjzE2vn3/8GRwWOs917YE4=';"); + _context.Response.Headers.ContentSecurityPolicy.First().ShouldContain($"style-src '{IdentityServerConstants.ContentSecurityPolicyHashes.EndSessionStyle}';"); _context.Response.Headers.ContentSecurityPolicy.First().ShouldContain("frame-src http://foo.com http://bar.com"); _context.Response.Headers["X-Content-Security-Policy"].First().ShouldContain("default-src 'none';"); - _context.Response.Headers["X-Content-Security-Policy"].First().ShouldContain("style-src 'sha256-e6FQZewefmod2S/5T11pTXjzE2vn3/8GRwWOs917YE4=';"); + _context.Response.Headers["X-Content-Security-Policy"].First().ShouldContain($"style-src '{IdentityServerConstants.ContentSecurityPolicyHashes.EndSessionStyle}';"); _context.Response.Headers["X-Content-Security-Policy"].First().ShouldContain("frame-src http://foo.com http://bar.com"); _context.Response.Body.Seek(0, SeekOrigin.Begin); using var rdr = new StreamReader(_context.Response.Body); @@ -64,6 +67,27 @@ public async Task success_should_render_html_and_iframes() html.ShouldContain(""); } + [Fact] + public async Task csp_hash_should_match_inline_style() + { + _result.IsError = false; + _result.FrontChannelLogoutUrls = new string[] { "http://foo.com", "http://bar.com" }; + + await _subject.WriteHttpResponse(new EndSessionCallbackResult(_result), _context); + + _context.Response.StatusCode.ShouldBe(200); + _context.Response.ContentType.ShouldStartWith("text/html"); + _context.Response.Body.Seek(0, SeekOrigin.Begin); + using var rdr = new StreamReader(_context.Response.Body); + var html = await rdr.ReadToEndAsync(); + + var match = Regex.Match(html, "", RegexOptions.Singleline | RegexOptions.IgnoreCase); + match.Success.ShouldBeTrue(); + + var styleSha256 = "sha256-" + match.Groups[1].Value.ToSha256(); + IdentityServerConstants.ContentSecurityPolicyHashes.EndSessionStyle.ShouldContain(styleSha256); + } + [Fact] public async Task fsuccess_should_add_unsafe_inline_for_csp_level_1() { @@ -73,8 +97,8 @@ public async Task fsuccess_should_add_unsafe_inline_for_csp_level_1() await _subject.WriteHttpResponse(new EndSessionCallbackResult(_result), _context); - _context.Response.Headers.ContentSecurityPolicy.First().ShouldContain("style-src 'unsafe-inline' 'sha256-e6FQZewefmod2S/5T11pTXjzE2vn3/8GRwWOs917YE4='"); - _context.Response.Headers["X-Content-Security-Policy"].First().ShouldContain("style-src 'unsafe-inline' 'sha256-e6FQZewefmod2S/5T11pTXjzE2vn3/8GRwWOs917YE4='"); + _context.Response.Headers.ContentSecurityPolicy.First().ShouldContain($"style-src 'unsafe-inline' '{IdentityServerConstants.ContentSecurityPolicyHashes.EndSessionStyle}'"); + _context.Response.Headers["X-Content-Security-Policy"].First().ShouldContain($"style-src 'unsafe-inline' '{IdentityServerConstants.ContentSecurityPolicyHashes.EndSessionStyle}'"); } [Fact] @@ -86,7 +110,7 @@ public async Task form_post_mode_should_not_add_deprecated_header_when_it_is_dis await _subject.WriteHttpResponse(new EndSessionCallbackResult(_result), _context); - _context.Response.Headers.ContentSecurityPolicy.First().ShouldContain("style-src 'sha256-e6FQZewefmod2S/5T11pTXjzE2vn3/8GRwWOs917YE4='"); + _context.Response.Headers.ContentSecurityPolicy.First().ShouldContain($"style-src '{IdentityServerConstants.ContentSecurityPolicyHashes.EndSessionStyle}'"); _context.Response.Headers["X-Content-Security-Policy"].ShouldBeEmpty(); } }