Browse Source

Re-generate tokens in CookieCsrfTokenRepository

Fixes support for re-generating tokens within a request such as when
CsrfAuthenticationStrategy removes a null token and saves an empty
cookie value on the response.

Closes gh-12141
pull/12137/head
Steve Riesenberg 3 years ago
parent
commit
6b0ed0205b
No known key found for this signature in database
GPG Key ID: 5F311AB48A55D521
  1. 17
      web/src/main/java/org/springframework/security/web/csrf/CookieCsrfTokenRepository.java
  2. 26
      web/src/test/java/org/springframework/security/web/csrf/CookieCsrfTokenRepositoryTests.java

17
web/src/main/java/org/springframework/security/web/csrf/CookieCsrfTokenRepository.java

@ -43,6 +43,9 @@ public final class CookieCsrfTokenRepository implements CsrfTokenRepository { @@ -43,6 +43,9 @@ public final class CookieCsrfTokenRepository implements CsrfTokenRepository {
static final String DEFAULT_CSRF_HEADER_NAME = "X-XSRF-TOKEN";
private static final String CSRF_TOKEN_REMOVED_ATTRIBUTE_NAME = CookieCsrfTokenRepository.class.getName()
.concat(".REMOVED");
private String parameterName = DEFAULT_CSRF_PARAMETER_NAME;
private String headerName = DEFAULT_CSRF_HEADER_NAME;
@ -79,10 +82,24 @@ public final class CookieCsrfTokenRepository implements CsrfTokenRepository { @@ -79,10 +82,24 @@ public final class CookieCsrfTokenRepository implements CsrfTokenRepository {
cookie.setDomain(this.cookieDomain);
}
response.addCookie(cookie);
// Set request attribute to signal that response has blank cookie value,
// which allows loadToken to return null when token has been removed
if (!StringUtils.hasLength(tokenValue)) {
request.setAttribute(CSRF_TOKEN_REMOVED_ATTRIBUTE_NAME, Boolean.TRUE);
}
else {
request.removeAttribute(CSRF_TOKEN_REMOVED_ATTRIBUTE_NAME);
}
}
@Override
public CsrfToken loadToken(HttpServletRequest request) {
// Return null when token has been removed during the current request
// which allows loadDeferredToken to re-generate the token
if (Boolean.TRUE.equals(request.getAttribute(CSRF_TOKEN_REMOVED_ATTRIBUTE_NAME))) {
return null;
}
Cookie cookie = WebUtils.getCookie(request, this.cookieName);
if (cookie == null) {
return null;

26
web/src/test/java/org/springframework/security/web/csrf/CookieCsrfTokenRepositoryTests.java

@ -263,6 +263,32 @@ public class CookieCsrfTokenRepositoryTests { @@ -263,6 +263,32 @@ public class CookieCsrfTokenRepositoryTests {
assertThat(tokenCookie.isHttpOnly()).isEqualTo(true);
}
@Test
public void loadDeferredTokenWhenExistsAndNullSavedThenGeneratedAndSaved() {
CsrfToken generatedToken = this.repository.generateToken(this.request);
this.request
.setCookies(new Cookie(CookieCsrfTokenRepository.DEFAULT_CSRF_COOKIE_NAME, generatedToken.getToken()));
this.repository.saveToken(null, this.request, this.response);
DeferredCsrfToken deferredCsrfToken = this.repository.loadDeferredToken(this.request, this.response);
CsrfToken csrfToken = deferredCsrfToken.get();
assertThat(csrfToken).isNotNull();
assertThat(generatedToken).isNotEqualTo(csrfToken);
assertThat(deferredCsrfToken.isGenerated()).isTrue();
}
@Test
public void loadDeferredTokenWhenExistsAndNullSavedAndNonNullSavedThenLoaded() {
CsrfToken generatedToken = this.repository.generateToken(this.request);
this.request
.setCookies(new Cookie(CookieCsrfTokenRepository.DEFAULT_CSRF_COOKIE_NAME, generatedToken.getToken()));
this.repository.saveToken(null, this.request, this.response);
this.repository.saveToken(generatedToken, this.request, this.response);
DeferredCsrfToken deferredCsrfToken = this.repository.loadDeferredToken(this.request, this.response);
CsrfToken csrfToken = deferredCsrfToken.get();
assertThatCsrfToken(csrfToken).isEqualTo(generatedToken);
assertThat(deferredCsrfToken.isGenerated()).isFalse();
}
@Test
public void loadDeferredTokenWhenExistsThenLoaded() {
CsrfToken generatedToken = this.repository.generateToken(this.request);

Loading…
Cancel
Save