Skip to content

Commit

Permalink
Xor CSRF token (#6798)
Browse files Browse the repository at this point in the history
#### What type of PR is this?

/kind improvement
/area core
/milestone 2.20.x

#### What this PR does / why we need it:

This PR makes XOR operation for CSRF token and changes the CSRF cookie `HttpOnly` to `true` to forbid JavaScript from accessing the cookie.

See https://docs.spring.io/spring-security/reference/servlet/exploits/csrf.html#csrf-token-request-handler-breach for more details.

#### Special notes for your reviewer:

```bash
http http://localhost:8090/login -ph

HTTP/1.1 200 OK
set-cookie: XSRF-TOKEN=6d5dd83f-f0a7-4d94-a33e-73f213d679ff; Path=/; HTTPOnly
```

```bash
http http://localhost:8090/login -pb | grep _csrf

><input type="hidden" name="_csrf" value="ctubmrEC3dAbxC5H_k_-VnVUtih2BrfjcPfLmVAyaP0a1kAdEb-t_IcwuLM29B11yGLKNRQxm0lFZILOFZX-_GcHWJ974iR5"/>
```

#### Does this PR introduce a user-facing change?

```release-note
None
```
  • Loading branch information
JohnNiang authored Oct 9, 2024
1 parent 8458939 commit 5c50779
Show file tree
Hide file tree
Showing 2 changed files with 11 additions and 12 deletions.
Original file line number Diff line number Diff line change
@@ -1,30 +1,29 @@
package run.halo.app.security;

import static org.springframework.security.web.server.csrf.CookieServerCsrfTokenRepository.withHttpOnlyFalse;
import static org.springframework.security.web.server.util.matcher.ServerWebExchangeMatchers.pathMatchers;

import org.springframework.security.config.web.server.ServerHttpSecurity;
import org.springframework.security.web.server.csrf.CookieServerCsrfTokenRepository;
import org.springframework.security.web.server.csrf.CsrfWebFilter;
import org.springframework.security.web.server.csrf.ServerCsrfTokenRequestAttributeHandler;
import org.springframework.security.web.server.csrf.XorServerCsrfTokenRequestAttributeHandler;
import org.springframework.security.web.server.util.matcher.AndServerWebExchangeMatcher;
import org.springframework.security.web.server.util.matcher.NegatedServerWebExchangeMatcher;
import org.springframework.stereotype.Component;
import run.halo.app.security.authentication.SecurityConfigurer;

@Component
public class CsrfConfigurer implements SecurityConfigurer {
class CsrfConfigurer implements SecurityConfigurer {

@Override
public void configure(ServerHttpSecurity http) {
var csrfMatcher = new AndServerWebExchangeMatcher(
CsrfWebFilter.DEFAULT_CSRF_MATCHER,
new NegatedServerWebExchangeMatcher(pathMatchers("/api/**", "/apis/**", "/system/setup")
));
new NegatedServerWebExchangeMatcher(
pathMatchers("/api/**", "/apis/**", "/system/setup"))
);
http.csrf(csrfSpec -> csrfSpec
.csrfTokenRepository(withHttpOnlyFalse())
// TODO Use XorServerCsrfTokenRequestAttributeHandler instead when console implements
// the algorithm
.csrfTokenRequestHandler(new ServerCsrfTokenRequestAttributeHandler())
.csrfTokenRepository(new CookieServerCsrfTokenRepository())
.csrfTokenRequestHandler(new XorServerCsrfTokenRequestAttributeHandler())
.requireCsrfProtectionMatcher(csrfMatcher));
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package run.halo.app.infra.exception.handlers;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.springframework.security.test.web.reactive.server.SecurityMockServerConfigurers.csrf;

import java.util.Locale;
import org.junit.jupiter.api.AfterEach;
Expand Down Expand Up @@ -121,9 +122,8 @@ void shouldGetErrorIfThrowingGeneralException() {

@Test
void shouldGetConflictError() {
webClient.put().uri("/response-entity/conflict-error")
.header("X-XSRF-TOKEN", "fake-token")
.cookie("XSRF-TOKEN", "fake-token")
webClient.mutate().apply(csrf()).build()
.put().uri("/response-entity/conflict-error")
.exchange()
.expectStatus().isEqualTo(HttpStatus.CONFLICT)
.expectBody(ProblemDetail.class)
Expand Down

0 comments on commit 5c50779

Please sign in to comment.