From a499512f351bf063c796eb91b698e63856c0ac40 Mon Sep 17 00:00:00 2001 From: Stian Thorgersen Date: Tue, 9 Apr 2024 12:29:19 +0200 Subject: [PATCH] Set SameSite for all cookies (#28467) Closes #28465 Signed-off-by: stianst --- .../documentation/release_notes/topics/25_0_0.adoc | 14 ++++++++++++++ .../main/java/org/keycloak/cookie/CookieScope.java | 3 +++ .../main/java/org/keycloak/cookie/CookieType.java | 10 +++++----- .../cookies/DefaultCookieProviderTest.java | 10 +++++----- 4 files changed, 27 insertions(+), 10 deletions(-) diff --git a/docs/documentation/release_notes/topics/25_0_0.adoc b/docs/documentation/release_notes/topics/25_0_0.adoc index aa0c6943a7..e4736ee20b 100644 --- a/docs/documentation/release_notes/topics/25_0_0.adoc +++ b/docs/documentation/release_notes/topics/25_0_0.adoc @@ -11,6 +11,20 @@ better security, with almost the same CPU time as previous releases of {project_ memory, which is a requirement to be resistant against GPU attacks. The defaults for Argon2 in {project_name} requires 7MB per-hashing request. += SameSite attribute set for all cookies + +The following cookies did not use to set the `SameSite` attribute, which in recent browser versions results in them +defaulting to `SameSite=Lax`: + +* `KC_STATE_CHECKER` now sets `SameSite=Strict` +* `KC_RESTART` now sets `SameSite=None` +* `KC_AUTH_STATE` now sets `SameSite=Strict` +* `KEYCLOAK_LOCALE` now sets `SameSite=None` +* `KEYCLOAK_REMEMBER_ME` now sets `SameSite=None` + +The default value `SameSite=Lax` causes issues with POST based bindings, mostly applicable to SAML, but also used in +some OpenID Connect / OAuth 2.0 flows. + = Deprecated cookie methods removed The following methods for setting custom cookies have been removed: diff --git a/server-spi-private/src/main/java/org/keycloak/cookie/CookieScope.java b/server-spi-private/src/main/java/org/keycloak/cookie/CookieScope.java index fa8f16853c..34e41f03a2 100644 --- a/server-spi-private/src/main/java/org/keycloak/cookie/CookieScope.java +++ b/server-spi-private/src/main/java/org/keycloak/cookie/CookieScope.java @@ -6,6 +6,9 @@ public enum CookieScope { // Internal cookies are only available for direct requests to Keycloak INTERNAL(NewCookie.SameSite.STRICT, true), + // Internal cookies that are also available from JavaScript + INTERNAL_JS(NewCookie.SameSite.STRICT, false), + // Federation cookies are available after redirect from applications, and are also available in an iframe context // unless the browser blocks third-party cookies FEDERATION(NewCookie.SameSite.NONE, true), diff --git a/server-spi-private/src/main/java/org/keycloak/cookie/CookieType.java b/server-spi-private/src/main/java/org/keycloak/cookie/CookieType.java index cd75afe77d..7a81fb5dfb 100644 --- a/server-spi-private/src/main/java/org/keycloak/cookie/CookieType.java +++ b/server-spi-private/src/main/java/org/keycloak/cookie/CookieType.java @@ -5,11 +5,11 @@ import jakarta.annotation.Nullable; public final class CookieType { public static final CookieType AUTH_DETACHED = CookieType.create("KC_STATE_CHECKER") - .scope(CookieScope.LEGACY) + .scope(CookieScope.INTERNAL) .build(); public static final CookieType AUTH_RESTART = CookieType.create("KC_RESTART") - .scope(CookieScope.LEGACY) + .scope(CookieScope.FEDERATION) .defaultMaxAge(CookieMaxAge.SESSION) .build(); @@ -20,7 +20,7 @@ public final class CookieType { .build(); public static final CookieType AUTH_STATE = CookieType.create("KC_AUTH_STATE") - .scope(CookieScope.LEGACY_JS) + .scope(CookieScope.INTERNAL_JS) .build(); public static final CookieType IDENTITY = CookieType.create("KEYCLOAK_IDENTITY") @@ -29,12 +29,12 @@ public final class CookieType { .build(); public static final CookieType LOCALE = CookieType.create("KEYCLOAK_LOCALE") - .scope(CookieScope.LEGACY) + .scope(CookieScope.FEDERATION) .defaultMaxAge(CookieMaxAge.SESSION) .build(); public static final CookieType LOGIN_HINT = CookieType.create("KEYCLOAK_REMEMBER_ME") - .scope(CookieScope.LEGACY) + .scope(CookieScope.FEDERATION) .defaultMaxAge(CookieMaxAge.YEAR) .build(); diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/cookies/DefaultCookieProviderTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/cookies/DefaultCookieProviderTest.java index ea4e98a5ba..03f0c5b6c4 100644 --- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/cookies/DefaultCookieProviderTest.java +++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/cookies/DefaultCookieProviderTest.java @@ -53,12 +53,12 @@ public class DefaultCookieProviderTest extends AbstractKeycloakTest { }); Assert.assertEquals(12, response.getCookies().size()); assertCookie(response, "AUTH_SESSION_ID", "my-auth-session-id", "/auth/realms/master/", -1, false, true, "None", true); - assertCookie(response, "KC_AUTH_STATE", "my-auth-state", "/auth/realms/master/", 111, false, false, null, false); - assertCookie(response, "KC_RESTART", "my-auth-restart", "/auth/realms/master/", -1, false, true, null, false); - assertCookie(response, "KC_STATE_CHECKER", "my-auth-detached", "/auth/realms/master/", 222, false, true, null, false); + assertCookie(response, "KC_AUTH_STATE", "my-auth-state", "/auth/realms/master/", 111, false, false, "Strict", false); + assertCookie(response, "KC_RESTART", "my-auth-restart", "/auth/realms/master/", -1, false, true, "None", false); + assertCookie(response, "KC_STATE_CHECKER", "my-auth-detached", "/auth/realms/master/", 222, false, true, "Strict", false); assertCookie(response, "KEYCLOAK_IDENTITY", "my-identity", "/auth/realms/master/", 333, false, true, "None", true); - assertCookie(response, "KEYCLOAK_LOCALE", "my-locale", "/auth/realms/master/", -1, false, true, null, false); - assertCookie(response, "KEYCLOAK_REMEMBER_ME", "my-username", "/auth/realms/master/", 31536000, false, true, null, false); + assertCookie(response, "KEYCLOAK_LOCALE", "my-locale", "/auth/realms/master/", -1, false, true, "None", false); + assertCookie(response, "KEYCLOAK_REMEMBER_ME", "my-username", "/auth/realms/master/", 31536000, false, true, "None", false); assertCookie(response, "KEYCLOAK_SESSION", "my-session", "/auth/realms/master/", 444, false, false, "None", true); assertCookie(response, "WELCOME_STATE_CHECKER", "my-welcome-csrf", "/auth/realms/master/testing/run-on-server", 300, false, true, "Strict", false); }