보안 취약점으로 CSRF가 걸리게 될 경우, 무엇을 안해서 걸린 것일까.
웹 애플리케이션에서 CSRF(Cross-Site Request Forgery) 공격을 방지하는 것은 중요하다. 스프링 시큐리티(Spring Security)는 기본적으로 CSRF 공격을 방지하는 기능을 제공한다. 이번 글에서는 스프링 시큐리티에서 CSRF 보호를 위한 설정을 어떻게 구성할 수 있는지, 그리고 어떻게 CSRF 토큰을 쿠키에 저장하고, SPA(Single Page Application) 환경에서 이를 처리하는지에 대해 알아보겠다.
1. CSRF란?
CSRF(Cross-Site Request Forgery)는 웹 애플리케이션에서 발생할 수 있는 보안 취약점 중 하나로, 사용자가 의도하지 않은 요청을 통해 악의적인 행위를 수행하게 만드는 공격 기법이다. 이를 방지하기 위해 CSRF 토큰을 사용하여 요청의 출처를 확인하고, 토큰을 검증하여 악의적인 요청을 차단할 수 있다.
2. 기본적인 CSRF 설정
스프링 시큐리티에서 CSRF 보호는 기본적으로 활성화되어 있다. 하지만 API나 특정 요청 경로에 대해서는 CSRF 보호를 비활성화해야 할 경우가 있을 수 있다. 이를 위해 다음과 같은 설정을 사용할 수 있다.
private void csrfConfig() throws Exception {
httpSecurity.csrf(csrf -> csrf
.ignoringRequestMatchers(new AntPathRequestMatcher("/api/**")) // API 경로에 대해 CSRF 비활성화
.csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse()) // CSRF 토큰을 쿠키에 저장
.csrfTokenRequestHandler(new SpaCsrfTokenRequestHandler()) // SPA 환경에서 CSRF 토큰 처리 핸들러 설정
)
.addFilterAfter(new CsrfCookieFilter(), BasicAuthenticationFilter.class); // 필터 체인에 커스텀 필터 추가
}
위 코드는 다음과 같은 주요 기능을 한다:
- /api/** 경로에 대해서는 CSRF 검증을 비활성화한다.
- CSRF 토큰을 쿠키에 저장하여 클라이언트 측에서 이를 쉽게 사용할 수 있다.
- SpaCsrfTokenRequestHandler를 통해 SPA 환경에서 CSRF 토큰을 적절히 처리할 수 있도록 한다.
- CsrfCookieFilter를 추가하여 CSRF 토큰이 항상 쿠키에 포함되도록 보장한다.
3. SPA 환경에서 CSRF 처리
SPA(Single Page Application)에서는 페이지 이동 없이 모든 요청이 이루어지기 때문에 CSRF 토큰을 쿠키에 저장하고, 이를 클라이언트에서 헤더로 전송하는 방식으로 처리해야 한다. 이를 위해 커스텀 핸들러와 필터를 구성할 수 있다.
SpaCsrfTokenRequestHandler 클래스
이 핸들러는 CSRF 토큰을 처리하며, 요청 헤더에 토큰이 있을 경우 해당 값을 사용하고, 그렇지 않을 경우 요청 파라미터에서 토큰을 가져오는 역할을 한다.
final class SpaCsrfTokenRequestHandler extends CsrfTokenRequestAttributeHandler implements CsrfTokenRequestHandler {
private final CsrfTokenRequestHandler delegate = new XorCsrfTokenRequestAttributeHandler();
@Override
public void handle(HttpServletRequest request, HttpServletResponse response, Supplier<CsrfToken> csrfToken) {
this.delegate.handle(request, response, csrfToken); // BREACH 보호를 위해 XorCsrfTokenRequestAttributeHandler 사용
}
@Override
public String resolveCsrfTokenValue(HttpServletRequest request, CsrfToken csrfToken) {
if (StringUtils.hasText(request.getHeader(csrfToken.getHeaderName()))) {
return super.resolveCsrfTokenValue(request, csrfToken); // 요청 헤더에서 CSRF 토큰 확인
}
return this.delegate.resolveCsrfTokenValue(request, csrfToken); // 요청 파라미터에서 CSRF 토큰 확인
}
}
CsrfCookieFilter 클래스
이 필터는 각 요청마다 CSRF 토큰이 쿠키에 포함되도록 한다. 이를 통해 SPA에서 CSRF 토큰이 항상 유효하게 유지될 수 있다.
final class CsrfCookieFilter extends OncePerRequestFilter {
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
throws ServletException, IOException {
CsrfToken csrfToken = (CsrfToken) request.getAttribute("_csrf");
if (csrfToken != null) {
csrfToken.getToken(); // CSRF 토큰을 강제로 로드하여 쿠키에 추가
}
filterChain.doFilter(request, response);
}
}
4. 로그아웃 설정
CSRF 보호를 위해 로그아웃 요청도 POST 메서드로 처리해야 한다. 이를 위해 `<form>` 태그를 사용하여 POST 방식으로 로그아웃을 처리할 수 있다.
<form th:action="@{/api/logout}" th:method="post">
<input type="hidden" name="_csrf" th:value="${_csrf.token}" />
<button type="submit" class="btn btn-primary">로그아웃</button>
</form>
5. XSRF-TOKEN 생성
XSRF-TOKEN이 쿠키에 추가된 것을 확인할 수 있다.
6. 결론
이번 글에서는 스프링 시큐리티에서 CSRF 토큰을 설정하고 쿠키를 통해 처리하는 방법, 그리고 SPA 환경에서 CSRF 토큰을 어떻게 다룰 수 있는지에 대해 알아보았다. 특히, CSRF 토큰을 쿠키에 저장하고 이를 활용하여 SPA와의 통합을 원활하게 처리하는 방식에 대해 알아보았다. CSRF 공격을 방지하기 위해서는 모든 POST 요청에 대해 적절한 토큰 검증을 반드시 수행해야 하며, 로그아웃과 같은 민감한 요청에도 POST 방식과 CSRF 토큰을 적용하는 것이 중요하다.
'Spring > Security' 카테고리의 다른 글
Spring Security에서 세션값 변경 (0) | 2022.12.03 |
---|---|
Spring Security 로그인 문제 (0) | 2022.11.20 |
댓글