프로젝트에 적용한 security 에서 올바른 결과값을 반환해야 하는 API 가 403 에러를 리턴하는 것으로 확인되었다.
로그인 후 올바른 토큰을 헤더에 실어 시험 조회 요청을 보냈지만, 403 에러를 뱉은 것이다.
게다가 쿼리는 실행되는데, 로직의 break point 는 걸리지도 않았다.
☠️ csrf ?
구글링을 해본 결과 인가처리를 따로 하지 않은 시큐리티의 403 에러의 대부분은 csrf 때문으로 확인되었다.
CSRF(Cross-Site Request Forgery)는 웹 보안 취약점으로, 공격자가 사용자를 속여 의도하지 않은 작업을 수행하게 만드는 공격이다. 해당 공격은 다른 웹사이트 간의 상호 간섭을 방지하기 위해 설계된 동일 출처 정책을 부분적으로 우회할 수 있다.
웹 애플리케이션을 개발하기 위해 Thymeleaf 를 사용하는 경우가 많을 것이고 해당 템플릿은 csrf 토큰 기능을 지원한다.
하지만 Spring Security IDE 및 Postman에서 보낸 요청에서는 토큰이 없었기 때문에 다른 사이트의 올바르지 않은 요청으로 판단해 403 에러를 반환할 수 있다.
본 프로젝트는 REST API 서버로 설계되었기 때문에 진작에 csrf().disable() 메서드를 추가했었다.
때문에 해당 이유는 아닌 것으로 판단되어 다른 이유를 찾았다.
☠️ denyAll() ?
디버깅하며 에러를 추적해본 결과, 쿼리도 정상적으로 실행되고 토큰도 제대로 가져오지만 이상하게 요청이 자꾸 거부되는 것을 확인하였다.
일반적이지 않으므로 SecurityConfig 파일의 코드를 다시 찬찬히 살펴보았고 이상한 부분을 발견하였다.
.and()
.authorizeHttpRequests()
.requestMatchers(WHITE_LIST).permitAll()
.anyRequest().denyAll()
WHITE_LIST 의 엔드포인트로 들어오는 요청은 필터링을 거쳐야 하지 않기 때문에 permitAll() 메서드를 통해 허용시켰다.
정상적으로 작동하는 것을 확인하였다.
반면 문제는 이외 모든 요청에 있어서 denyAll() 로 되어 있었던 부분이였다.
인증처리를 해야하지 모든 요청을 거부하는 것은 아니였다.
자세히 찾아본 결과 .anyRequest().authenticated() 는 이전 규칙과 일치하지 않는 모든 요청이 인증된 사용자에 의해 전송된 경우에만 허용된다. 즉, 인증되지 않은 사용자가 이전 규칙과 일치하지 않는 요청을 전송하면 접근이 거부된다.
반면, .anyRequest().denyAll() 은 이전 규칙과 일치하지 않는 모든 요청이 거부된다. 즉, 인증된 사용자라도 이전 규칙과 일치하지 않는 요청을 전송하면 접근이 거부되었기 때문에 에러가 떴던 것이다.
해당 코드를 다음과 같이 수정하니 결과 값이 올바르게 나왔다.
.and()
.authorizeHttpRequests()
.requestMatchers(WHITE_LIST).permitAll()
.anyRequest().authenticated()