From 6e91786f9243588c8984fbaafaad0b5bcfe943c4 Mon Sep 17 00:00:00 2001 From: Luke Taylor Date: Mon, 9 May 2011 13:36:23 +0100 Subject: [PATCH] SEC-1734: AbstractRememberMeServices will now default to using a secure cookie if the connection is secure. The behaviour can be overridden by setting the useSecureCookie property in which case the cookie will either always be secure (true) or never (false). --- .../security/config/spring-security-3.1.rnc | 2 +- .../security/config/spring-security-3.1.xsd | 2 +- .../manual/src/docbook/appendix-namespace.xml | 19 +++++++++--- .../AbstractRememberMeServices.java | 30 ++++++++++++++----- .../AbstractRememberMeServicesTests.java | 8 ++--- 5 files changed, 43 insertions(+), 18 deletions(-) diff --git a/config/src/main/resources/org/springframework/security/config/spring-security-3.1.rnc b/config/src/main/resources/org/springframework/security/config/spring-security-3.1.rnc index 7377955b6c..dda2a172a5 100644 --- a/config/src/main/resources/org/springframework/security/config/spring-security-3.1.rnc +++ b/config/src/main/resources/org/springframework/security/config/spring-security-3.1.rnc @@ -537,7 +537,7 @@ remember-me.attlist &= attribute services-alias {xsd:token}? remember-me.attlist &= - ## Determines whether the "secure" flag will be set on the remember-me cookie. If set to true, the cookie will only be submitted over HTTPS. Defaults to false. + ## Determines whether the "secure" flag will be set on the remember-me cookie. If set to true, the cookie will only be submitted over HTTPS (recommended). By default, secure cookies will be used if the request is made on a secure connection. attribute use-secure-cookie {xsd:boolean}? remember-me.attlist &= diff --git a/config/src/main/resources/org/springframework/security/config/spring-security-3.1.xsd b/config/src/main/resources/org/springframework/security/config/spring-security-3.1.xsd index 1ba7bb1f43..f1354fa1c1 100644 --- a/config/src/main/resources/org/springframework/security/config/spring-security-3.1.xsd +++ b/config/src/main/resources/org/springframework/security/config/spring-security-3.1.xsd @@ -1172,7 +1172,7 @@ - Determines whether the "secure" flag will be set on the remember-me cookie. If set to true, the cookie will only be submitted over HTTPS. Defaults to false. + Determines whether the "secure" flag will be set on the remember-me cookie. If set to true, the cookie will only be submitted over HTTPS (recommended). By default, secure cookies will be used if the request is made on a secure connection. diff --git a/docs/manual/src/docbook/appendix-namespace.xml b/docs/manual/src/docbook/appendix-namespace.xml index b3d8040038..2bdfc09194 100644 --- a/docs/manual/src/docbook/appendix-namespace.xml +++ b/docs/manual/src/docbook/appendix-namespace.xml @@ -72,9 +72,9 @@
<literal>jaas-api-provision</literal> - If available, runs the request as the Subject acquired from - the JaasAuthenticationToken which is implemented by - adding a JaasApiIntegrationFilter bean to the stack. + If available, runs the request as the Subject acquired from + the JaasAuthenticationToken which is implemented by + adding a JaasApiIntegrationFilter bean to the stack. Defaults to "false".
@@ -334,7 +334,7 @@ Allows complete control of the RememberMeServices implementation that will be used by the filter. The value should be the id of a bean in the application - context which implements this interface. Should also implement + context which implements this interface. Should also implement LogoutHandler if a logout filter is in use.
@@ -369,6 +369,17 @@ and used automatically by the namespace configuration. If there are multiple instances, you can specify a bean id explicitly using this attribute.
+
+ <literal>use-secure-cookie</literal> + It is recommended that remember-me cookies are only submitted over HTTPS and thus should + be flagged as secure. By default, a secure cookie will be used if the + connection over which the login request is made is secure (as it should be). + If you set this property to false, secure cookies will not be used. + Setting it to true will always set the secure flag on the cookie. + This attribute maps to the useSecureCookie property of + AbstractRememberMeServices. + +
<literal>authentication-success-handler-ref</literal> Sets the authenticationSuccessHandler property on the diff --git a/web/src/main/java/org/springframework/security/web/authentication/rememberme/AbstractRememberMeServices.java b/web/src/main/java/org/springframework/security/web/authentication/rememberme/AbstractRememberMeServices.java index ca4c6d0ae0..cbf842874a 100644 --- a/web/src/main/java/org/springframework/security/web/authentication/rememberme/AbstractRememberMeServices.java +++ b/web/src/main/java/org/springframework/security/web/authentication/rememberme/AbstractRememberMeServices.java @@ -56,7 +56,7 @@ public abstract class AbstractRememberMeServices implements RememberMeServices, private boolean alwaysRemember; private String key; private int tokenValiditySeconds = TWO_WEEKS_S; - private boolean useSecureCookie = false; + private Boolean useSecureCookie = null; private GrantedAuthoritiesMapper authoritiesMapper = new NullAuthoritiesMapper(); public void afterPropertiesSet() throws Exception { @@ -296,9 +296,6 @@ public abstract class AbstractRememberMeServices implements RememberMeServices, /** * Sets a "cancel cookie" (with maxAge = 0) on the response to disable persistent logins. - * - * @param request - * @param response */ protected void cancelCookie(HttpServletRequest request, HttpServletResponse response) { logger.debug("Cancelling cookie"); @@ -310,7 +307,11 @@ public abstract class AbstractRememberMeServices implements RememberMeServices, } /** - * Sets the cookie on the response + * Sets the cookie on the response. + * + * By default a secure cookie will be used if the connection is secure. You can set the {@code useSecureCookie} + * property to {@code false} to override this. If you set it to {@code true}, the cookie will always be flagged + * as secure. * * @param tokens the tokens which will be encoded to make the cookie value. * @param maxAge the value passed to {@link Cookie#setMaxAge(int)} @@ -322,7 +323,13 @@ public abstract class AbstractRememberMeServices implements RememberMeServices, Cookie cookie = new Cookie(cookieName, cookieValue); cookie.setMaxAge(maxAge); cookie.setPath(getCookiePath(request)); - cookie.setSecure(useSecureCookie); + + if (useSecureCookie == null) { + cookie.setSecure(request.isSecure()); + } else { + cookie.setSecure(useSecureCookie); + } + response.addCookie(cookie); } @@ -332,7 +339,7 @@ public abstract class AbstractRememberMeServices implements RememberMeServices, } /** - * Implementation of LogoutHandler. Default behaviour is to call cancelCookie(). + * Implementation of {@code LogoutHandler}. Default behaviour is to call {@code cancelCookie()}. */ public void logout(HttpServletRequest request, HttpServletResponse response, Authentication authentication) { if (logger.isDebugEnabled()) { @@ -395,6 +402,15 @@ public abstract class AbstractRememberMeServices implements RememberMeServices, return tokenValiditySeconds; } + /** + * Whether the cookie should be flagged as secure or not. Secure cookies can only be sent over an HTTPS connection + * and this cannot be accidentally submitted over HTTP where they could be intercepted. + *

+ * By default the cookie will be secure if the request is secure. If you only want to use remember-me over + * HTTPS (recommended) you should set this property to {@code true}. + * + * @param useSecureCookie set to {@code true} to always user secure cookies, {@code false} to disable their use. + */ public void setUseSecureCookie(boolean useSecureCookie) { this.useSecureCookie = useSecureCookie; } diff --git a/web/src/test/java/org/springframework/security/web/authentication/rememberme/AbstractRememberMeServicesTests.java b/web/src/test/java/org/springframework/security/web/authentication/rememberme/AbstractRememberMeServicesTests.java index 0d5cf45876..402fcf2cf6 100644 --- a/web/src/test/java/org/springframework/security/web/authentication/rememberme/AbstractRememberMeServicesTests.java +++ b/web/src/test/java/org/springframework/security/web/authentication/rememberme/AbstractRememberMeServicesTests.java @@ -106,7 +106,7 @@ public class AbstractRememberMeServicesTests { request = new MockHttpServletRequest(); response = new MockHttpServletResponse(); // set non-login cookie - request.setCookies(new Cookie[] {new Cookie("mycookie", "cookie")}); + request.setCookies(new Cookie("mycookie", "cookie")); assertNull(services.autoLogin(request, response)); assertNull(response.getCookie(AbstractRememberMeServices.SPRING_SECURITY_REMEMBER_ME_COOKIE_KEY)); } @@ -134,8 +134,7 @@ public class AbstractRememberMeServicesTests { MockHttpServletRequest request = new MockHttpServletRequest(); MockHttpServletResponse response = new MockHttpServletResponse(); - request.setCookies(new Cookie[] { - new Cookie(AbstractRememberMeServices.SPRING_SECURITY_REMEMBER_ME_COOKIE_KEY, "ZZZ")}); + request.setCookies(new Cookie(AbstractRememberMeServices.SPRING_SECURITY_REMEMBER_ME_COOKIE_KEY, "ZZZ")); Authentication result = services.autoLogin(request, response); assertNull(result); assertCookieCancelled(response); @@ -147,8 +146,7 @@ public class AbstractRememberMeServicesTests { MockHttpServletRequest request = new MockHttpServletRequest(); MockHttpServletResponse response = new MockHttpServletResponse(); - request.setCookies(new Cookie[] { - new Cookie(AbstractRememberMeServices.SPRING_SECURITY_REMEMBER_ME_COOKIE_KEY, "")}); + request.setCookies(new Cookie(AbstractRememberMeServices.SPRING_SECURITY_REMEMBER_ME_COOKIE_KEY, "")); Authentication result = services.autoLogin(request, response); assertNull(result); assertCookieCancelled(response);