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).

This commit is contained in:
Luke Taylor 2011-05-09 13:36:23 +01:00
parent 396eced291
commit 6e91786f92
5 changed files with 43 additions and 18 deletions

View File

@ -537,7 +537,7 @@ remember-me.attlist &=
attribute services-alias {xsd:token}? attribute services-alias {xsd:token}?
remember-me.attlist &= 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}? attribute use-secure-cookie {xsd:boolean}?
remember-me.attlist &= remember-me.attlist &=

View File

@ -1172,7 +1172,7 @@
</xs:attribute> </xs:attribute>
<xs:attribute name="use-secure-cookie" type="xs:boolean"> <xs:attribute name="use-secure-cookie" type="xs:boolean">
<xs:annotation> <xs:annotation>
<xs:documentation>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.</xs:documentation> <xs:documentation>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.</xs:documentation>
</xs:annotation> </xs:annotation>
</xs:attribute> </xs:attribute>
<xs:attribute name="token-validity-seconds" type="xs:integer"> <xs:attribute name="token-validity-seconds" type="xs:integer">

View File

@ -369,6 +369,17 @@
and used automatically by the namespace configuration. If there are multiple and used automatically by the namespace configuration. If there are multiple
instances, you can specify a bean <literal>id</literal> explicitly using this attribute. </para> instances, you can specify a bean <literal>id</literal> explicitly using this attribute. </para>
</section> </section>
<section>
<title><literal>use-secure-cookie</literal></title>
<para>It is recommended that remember-me cookies are only submitted over HTTPS and thus should
be flagged as <quote>secure</quote>. 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 <literal>false</literal>, secure cookies will not be used.
Setting it to <literal>true</literal> will always set the secure flag on the cookie.
This attribute maps to the <literal>useSecureCookie</literal> property of
<classname>AbstractRememberMeServices</classname>.
</para>
</section>
<section> <section>
<title><literal>authentication-success-handler-ref</literal></title> <title><literal>authentication-success-handler-ref</literal></title>
<para>Sets the <code>authenticationSuccessHandler</code> property on the <para>Sets the <code>authenticationSuccessHandler</code> property on the

View File

@ -56,7 +56,7 @@ public abstract class AbstractRememberMeServices implements RememberMeServices,
private boolean alwaysRemember; private boolean alwaysRemember;
private String key; private String key;
private int tokenValiditySeconds = TWO_WEEKS_S; private int tokenValiditySeconds = TWO_WEEKS_S;
private boolean useSecureCookie = false; private Boolean useSecureCookie = null;
private GrantedAuthoritiesMapper authoritiesMapper = new NullAuthoritiesMapper(); private GrantedAuthoritiesMapper authoritiesMapper = new NullAuthoritiesMapper();
public void afterPropertiesSet() throws Exception { 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. * 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) { protected void cancelCookie(HttpServletRequest request, HttpServletResponse response) {
logger.debug("Cancelling cookie"); 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 tokens the tokens which will be encoded to make the cookie value.
* @param maxAge the value passed to {@link Cookie#setMaxAge(int)} * @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 cookie = new Cookie(cookieName, cookieValue);
cookie.setMaxAge(maxAge); cookie.setMaxAge(maxAge);
cookie.setPath(getCookiePath(request)); cookie.setPath(getCookiePath(request));
if (useSecureCookie == null) {
cookie.setSecure(request.isSecure());
} else {
cookie.setSecure(useSecureCookie); cookie.setSecure(useSecureCookie);
}
response.addCookie(cookie); response.addCookie(cookie);
} }
@ -332,7 +339,7 @@ public abstract class AbstractRememberMeServices implements RememberMeServices,
} }
/** /**
* Implementation of <tt>LogoutHandler</tt>. Default behaviour is to call <tt>cancelCookie()</tt>. * Implementation of {@code LogoutHandler}. Default behaviour is to call {@code cancelCookie()}.
*/ */
public void logout(HttpServletRequest request, HttpServletResponse response, Authentication authentication) { public void logout(HttpServletRequest request, HttpServletResponse response, Authentication authentication) {
if (logger.isDebugEnabled()) { if (logger.isDebugEnabled()) {
@ -395,6 +402,15 @@ public abstract class AbstractRememberMeServices implements RememberMeServices,
return tokenValiditySeconds; 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.
* <p>
* 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) { public void setUseSecureCookie(boolean useSecureCookie) {
this.useSecureCookie = useSecureCookie; this.useSecureCookie = useSecureCookie;
} }

View File

@ -106,7 +106,7 @@ public class AbstractRememberMeServicesTests {
request = new MockHttpServletRequest(); request = new MockHttpServletRequest();
response = new MockHttpServletResponse(); response = new MockHttpServletResponse();
// set non-login cookie // set non-login cookie
request.setCookies(new Cookie[] {new Cookie("mycookie", "cookie")}); request.setCookies(new Cookie("mycookie", "cookie"));
assertNull(services.autoLogin(request, response)); assertNull(services.autoLogin(request, response));
assertNull(response.getCookie(AbstractRememberMeServices.SPRING_SECURITY_REMEMBER_ME_COOKIE_KEY)); assertNull(response.getCookie(AbstractRememberMeServices.SPRING_SECURITY_REMEMBER_ME_COOKIE_KEY));
} }
@ -134,8 +134,7 @@ public class AbstractRememberMeServicesTests {
MockHttpServletRequest request = new MockHttpServletRequest(); MockHttpServletRequest request = new MockHttpServletRequest();
MockHttpServletResponse response = new MockHttpServletResponse(); MockHttpServletResponse response = new MockHttpServletResponse();
request.setCookies(new Cookie[] { request.setCookies(new Cookie(AbstractRememberMeServices.SPRING_SECURITY_REMEMBER_ME_COOKIE_KEY, "ZZZ"));
new Cookie(AbstractRememberMeServices.SPRING_SECURITY_REMEMBER_ME_COOKIE_KEY, "ZZZ")});
Authentication result = services.autoLogin(request, response); Authentication result = services.autoLogin(request, response);
assertNull(result); assertNull(result);
assertCookieCancelled(response); assertCookieCancelled(response);
@ -147,8 +146,7 @@ public class AbstractRememberMeServicesTests {
MockHttpServletRequest request = new MockHttpServletRequest(); MockHttpServletRequest request = new MockHttpServletRequest();
MockHttpServletResponse response = new MockHttpServletResponse(); MockHttpServletResponse response = new MockHttpServletResponse();
request.setCookies(new Cookie[] { request.setCookies(new Cookie(AbstractRememberMeServices.SPRING_SECURITY_REMEMBER_ME_COOKIE_KEY, ""));
new Cookie(AbstractRememberMeServices.SPRING_SECURITY_REMEMBER_ME_COOKIE_KEY, "")});
Authentication result = services.autoLogin(request, response); Authentication result = services.autoLogin(request, response);
assertNull(result); assertNull(result);
assertCookieCancelled(response); assertCookieCancelled(response);