From 90b29b0e31a0d184b458b6f8741ceaf6ef4e740e Mon Sep 17 00:00:00 2001 From: stianst Date: Tue, 26 May 2020 10:53:40 +0200 Subject: [PATCH] KEYCLOAK-14107 Admin page content blocked on v10.0.0 due to content security policy --- .../org/keycloak/models/BrowserSecurityHeaders.java | 8 ++++---- .../keycloak/models/BrowserSecurityHeadersTest.java | 2 +- .../services/resources/admin/AdminConsole.java | 3 ++- .../keycloak/testsuite/url/DefaultHostnameTest.java | 13 ++++++++++++- 4 files changed, 19 insertions(+), 7 deletions(-) diff --git a/server-spi-private/src/main/java/org/keycloak/models/BrowserSecurityHeaders.java b/server-spi-private/src/main/java/org/keycloak/models/BrowserSecurityHeaders.java index d966df2c53..53c1f12a27 100755 --- a/server-spi-private/src/main/java/org/keycloak/models/BrowserSecurityHeaders.java +++ b/server-spi-private/src/main/java/org/keycloak/models/BrowserSecurityHeaders.java @@ -97,9 +97,9 @@ public class BrowserSecurityHeaders { public static class ContentSecurityPolicyBuilder { - private String frameSrc = "self"; - private String frameAncestors = "self"; - private String objectSrc = "none"; + private String frameSrc = "'self'"; + private String frameAncestors = "'self'"; + private String objectSrc = "'none'"; private boolean first; private StringBuilder sb; @@ -136,7 +136,7 @@ public class BrowserSecurityHeaders { } first = false; - sb.append(k).append(" '").append(v).append("';"); + sb.append(k).append(" ").append(v).append(";"); } } diff --git a/server-spi-private/src/test/java/org/keycloak/models/BrowserSecurityHeadersTest.java b/server-spi-private/src/test/java/org/keycloak/models/BrowserSecurityHeadersTest.java index 92a34063a5..648925fb9b 100644 --- a/server-spi-private/src/test/java/org/keycloak/models/BrowserSecurityHeadersTest.java +++ b/server-spi-private/src/test/java/org/keycloak/models/BrowserSecurityHeadersTest.java @@ -11,7 +11,7 @@ public class BrowserSecurityHeadersTest { assertEquals("frame-src 'self'; frame-ancestors 'self'; object-src 'none';", BrowserSecurityHeaders.ContentSecurityPolicyBuilder.create().build()); assertEquals("frame-ancestors 'self'; object-src 'none';", BrowserSecurityHeaders.ContentSecurityPolicyBuilder.create().frameSrc(null).build()); assertEquals("frame-src 'self'; object-src 'none';", BrowserSecurityHeaders.ContentSecurityPolicyBuilder.create().frameAncestors(null).build()); - assertEquals("frame-src 'custom-frame-src'; frame-ancestors 'custom-frame-ancestors'; object-src 'none';", BrowserSecurityHeaders.ContentSecurityPolicyBuilder.create().frameSrc("custom-frame-src").frameAncestors("custom-frame-ancestors").build()); + assertEquals("frame-src 'custom-frame-src'; frame-ancestors 'custom-frame-ancestors'; object-src 'none';", BrowserSecurityHeaders.ContentSecurityPolicyBuilder.create().frameSrc("'custom-frame-src'").frameAncestors("'custom-frame-ancestors'").build()); } } diff --git a/services/src/main/java/org/keycloak/services/resources/admin/AdminConsole.java b/services/src/main/java/org/keycloak/services/resources/admin/AdminConsole.java index 9d93de6b62..0971bef00f 100644 --- a/services/src/main/java/org/keycloak/services/resources/admin/AdminConsole.java +++ b/services/src/main/java/org/keycloak/services/resources/admin/AdminConsole.java @@ -25,6 +25,7 @@ import javax.ws.rs.NotFoundException; import org.keycloak.Config; import org.keycloak.common.ClientConnection; import org.keycloak.common.Version; +import org.keycloak.common.util.UriUtils; import org.keycloak.headers.SecurityHeadersProvider; import org.keycloak.models.AdminRoles; import org.keycloak.models.ClientModel; @@ -309,7 +310,7 @@ public class AdminConsole { // Replace CSP if admin is hosted on different URL if (!adminBaseUri.equals(authServerBaseUri)) { - session.getProvider(SecurityHeadersProvider.class).options().allowFrameSrc(UriBuilder.fromUri(authServerBaseUri).replacePath("").build().toString()); + session.getProvider(SecurityHeadersProvider.class).options().allowFrameSrc(UriUtils.getOrigin(authServerBaseUri)); } return builder.build(); diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/url/DefaultHostnameTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/url/DefaultHostnameTest.java index 2398e1cc77..baa87db6af 100644 --- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/url/DefaultHostnameTest.java +++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/url/DefaultHostnameTest.java @@ -14,8 +14,10 @@ import org.keycloak.broker.provider.util.SimpleHttp; import org.keycloak.client.registration.Auth; import org.keycloak.client.registration.ClientRegistration; import org.keycloak.client.registration.ClientRegistrationException; +import org.keycloak.common.util.UriUtils; import org.keycloak.jose.jws.JWSInput; import org.keycloak.jose.jws.JWSInputException; +import org.keycloak.models.BrowserSecurityHeaders; import org.keycloak.protocol.oidc.representations.OIDCConfigurationRepresentation; import org.keycloak.representations.AccessToken; import org.keycloak.representations.JsonWebToken; @@ -246,12 +248,21 @@ public class DefaultHostnameTest extends AbstractHostnameTest { private void assertAdminPage(String realm, String expectedFrontendUrl, String expectedAdminUrl) throws IOException, URISyntaxException { try (CloseableHttpClient client = HttpClientBuilder.create().build()) { - String indexPage = SimpleHttp.doGet(AUTH_SERVER_ROOT + "/admin/" + realm +"/console/", client).asString(); + SimpleHttp.Response response = SimpleHttp.doGet(AUTH_SERVER_ROOT + "/admin/" + realm +"/console/", client).asResponse(); + String indexPage = response.asString(); assertTrue(indexPage.contains("authServerUrl = '" + expectedFrontendUrl +"'")); assertTrue(indexPage.contains("authUrl = '" + expectedAdminUrl +"'")); assertTrue(indexPage.contains("consoleBaseUrl = '" + new URI(expectedAdminUrl).getPath() +"/admin/" + realm + "/console/'")); assertTrue(indexPage.contains("resourceUrl = '" + new URI(expectedAdminUrl).getPath() +"/resources/")); + + String cspHeader = response.getFirstHeader(BrowserSecurityHeaders.CONTENT_SECURITY_POLICY); + + if (expectedFrontendUrl.equalsIgnoreCase(expectedAdminUrl)) { + assertEquals("frame-src 'self'; frame-ancestors 'self'; object-src 'none';", cspHeader); + } else { + assertEquals("frame-src " + UriUtils.getOrigin(expectedFrontendUrl) + "; frame-ancestors 'self'; object-src 'none';", cspHeader); + } } }