Update CookieCsrfTokenRepository docs to cookiHttpOnly=false

Currently CookieCsrfTokenRepository does not specify that the httpOnly
flag needs set to false. We should update the reference to include this
setting (and a comment about it) since it states that the settings will
work with AngularJS.

This commit updates the documentation and provides a convenience factory
method to create a CookieCsrfTokenRepository with cookiHttpOnly=false

Fixes gh-3865
This commit is contained in:
Rob Winch 2016-05-05 12:48:16 -05:00 committed by Joe Grandja
parent 64f53620f8
commit d4218c70f1
3 changed files with 48 additions and 6 deletions

View File

@ -3359,9 +3359,19 @@ You can configure `CookieCsrfTokenRepository` in XML using the following:
<!-- ... --> <!-- ... -->
<csrf token-repository-ref="tokenRepository"/> <csrf token-repository-ref="tokenRepository"/>
</http> </http>
<b:bean id="tokenRepository" class="org.springframework.security.web.csrf.CookieCsrfTokenRepository"/> <b:bean id="tokenRepository"
class="org.springframework.security.web.csrf.CookieCsrfTokenRepository"
p:cookieHttpOnly="false"/>
---- ----
[NOTE]
====
The sample explicitly sets `cookieHttpOnly=false`.
This is necessary to allow JavaScript (i.e. AngularJS) to read it.
If you do not need the ability to read the cookie with JavaScript directly, it is recommended to omit `cookieHttpOnly=false` to improve security.
====
You can configure `CookieCsrfTokenRepository` in Java Configuration using: You can configure `CookieCsrfTokenRepository` in Java Configuration using:
[source,java] [source,java]
@ -3374,11 +3384,18 @@ public class WebSecurityConfig extends
protected void configure(HttpSecurity http) throws Exception { protected void configure(HttpSecurity http) throws Exception {
http http
.csrf() .csrf()
.csrfTokenRepository(new CookieCsrfTokenRepository()); .csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse());
} }
} }
---- ----
[NOTE]
====
The sample explicitly sets `cookieHttpOnly=false`.
This is necessary to allow JavaScript (i.e. AngularJS) to read it.
If you do not need the ability to read the cookie with JavaScript directly, it is recommended to omit `cookieHttpOnly=false` (by using `new CookieCsrfTokenRepository()` instead) to improve security.
====
[[csrf-caveats]] [[csrf-caveats]]
=== CSRF Caveats === CSRF Caveats
@ -6594,9 +6611,9 @@ Spring Security provides `AuthenticationPrincipalArgumentResolver` which can aut
[source,xml] [source,xml]
---- ----
<mvc:annotation-driven> <mvc:annotation-driven>
<mvc:argument-resolvers> <mvc:argument-resolvers>
<bean class="org.springframework.security.web.method.annotation.AuthenticationPrincipalArgumentResolver" /> <bean class="org.springframework.security.web.method.annotation.AuthenticationPrincipalArgumentResolver" />
</mvc:argument-resolvers> </mvc:argument-resolvers>
</mvc:annotation-driven> </mvc:annotation-driven>
---- ----

View File

@ -31,7 +31,7 @@ import org.springframework.web.util.WebUtils;
/** /**
* A {@link CsrfTokenRepository} that persist the CSRF token in a cookie named * A {@link CsrfTokenRepository} that persist the CSRF token in a cookie named
* "XSRF-TOKEN" and reads from the header "X-XSRF-TOKEN" following the conventions of * "XSRF-TOKEN" and reads from the header "X-XSRF-TOKEN" following the conventions of
* AngularJS. * AngularJS. When using with AngularJS be sure to use {@link #withHttpOnlyFalse()}.
* *
* @author Rob Winch * @author Rob Winch
* @since 4.1 * @since 4.1
@ -153,6 +153,19 @@ public final class CookieCsrfTokenRepository implements CsrfTokenRepository {
return contextPath.length() > 0 ? contextPath : "/"; return contextPath.length() > 0 ? contextPath : "/";
} }
/**
* Factory method to conveniently create an instance that has
* {@link #setCookieHttpOnly(boolean)} set to false.
*
* @return and instance of CookieCsrfTokenRepository with
* {@link #setCookieHttpOnly(boolean)} set to false
*/
public static CookieCsrfTokenRepository withHttpOnlyFalse() {
CookieCsrfTokenRepository result = new CookieCsrfTokenRepository();
result.setCookieHttpOnly(false);
return result;
}
private String createNewToken() { private String createNewToken() {
return UUID.randomUUID().toString(); return UUID.randomUUID().toString();
} }

View File

@ -138,6 +138,18 @@ public class CookieCsrfTokenRepositoryTests {
assertThat(tokenCookie.isHttpOnly()).isFalse(); assertThat(tokenCookie.isHttpOnly()).isFalse();
} }
@Test
public void saveTokenWithHttpOnlyFalse() {
this.repository = CookieCsrfTokenRepository.withHttpOnlyFalse();
CsrfToken token = this.repository.generateToken(this.request);
this.repository.saveToken(token, this.request, this.response);
Cookie tokenCookie = this.response
.getCookie(CookieCsrfTokenRepository.DEFAULT_CSRF_COOKIE_NAME);
assertThat(tokenCookie.isHttpOnly()).isFalse();
}
@Test @Test
public void loadTokenNoCookiesNull() { public void loadTokenNoCookiesNull() {
assertThat(this.repository.loadToken(this.request)).isNull(); assertThat(this.repository.loadToken(this.request)).isNull();