[Spring security 6] Controller test 403 Forbidden 에러(feat. csrf)

Simple·2023년 6월 30일
0

트러블슈팅

목록 보기
11/13
post-thumbnail

문제 원인

Spring Boot 3.x 버전을 사용하면서 Spring security도 6.x를 사용하게 됐다.
그런데 Spring Boot 2.x 버전과 Spring security 5.x 버전을 사용하면서 나타나지 않았던 문제가 생겼다.

보통 csrf랑 연관되서 403 에러가 나지만, Jwt를 사용하기 때문에 SecurityConfig에서 이미 disable을 해준 상황이다.

그래서 Spring security 5.x -> Spring security 6.x 의 변화된 부분을
공식문서를 통해 확인한 결과

  • 더 이상 모든 요청에 대해 세션을 로드할 필요가 없으므로 성능 향상을 위해 CsrfToken의 로드가 기본적으로 지연됩니다.
  • 이제 CsrfToken은 BREACH 공격으로부터 CSRF 토큰을 보호하기 위해 기본적으로 모든 요청에 임의성을 포함합니다.

Spring security 5에서 자동으로 로드되던 것이 지연 로드, 무작위 포함이 추가 됐다.

해결 방안

실제 애플리케이션 환경에서는 csrf를 disable()을 해주었다면 문제가 안생긴다.
다만 테스트 환경에서는 적용이 안되므로 MockMvc에서 request에 자동으로 유효한 csrf를 제공해준다.

테스트 코드에는

ResultActions resultActions = mockMvc.perform(
                patch("/api/users").with(csrf()))

이렇게만 적용해줘도 문제가 해결된다.

그러나 모든 테스트 코드에 .with(csrf())를 붙여야 하므로 전역으로 적용할 수 있는 방법을 만든다.

spring security를 애플리케이션에 적용한 것이 전제이므로, test환경에 spring security를 적용했다면 setUp 코드가 있을 것이다.


protected MockMvc mockMvc;

@BeforeEach
public void setUp(WebApplicationContext webApplicationContext){
	this.mockMvc = MockMvcBuilders
    		.webAppContextSetup(webApplicationContext)       
    		...
            .apply(springSecurity())
            .defaultRequest(post("/**").with(csrf()))
            .defaultRequest(patch("/**").with(csrf()))
            .defaultRequest(delete("/**").with(csrf()))
            ...
}

이렇게 적용해주면 요청들에 일괄적용할 수 있다.

이것도 Controller Test 마다 만드는 것보다,
추상 클래스에 적용시켜서 test 대상이 되는 Controller가 상속받게끔 만들면 편하게 관리할 수 있다.

결과

참고 문서

https://docs.spring.io/spring-security/reference/servlet/exploits/csrf.html#csrf-testing

https://docs.spring.io/spring-security/reference/servlet/test/mockmvc/csrf.html

profile
몰입하는 개발자

1개의 댓글

comment-user-thumbnail
2023년 7월 3일

잘봤습니다

답글 달기