본문 바로가기

Language/Spring Security

CSRF 통합

728x90
  • CSRF 공격을 방지하기 위한 토큰 패턴을 사용하려면 실제 CSRF 토큰을 HTTP 요청에 포함해야 함
    → POST 방식으로 요청 시 반드시 토큰을 HTTP 요청에 포함
  • 그래서 브라우저에 의해 HTTP 요청에 자동으로 포함되지 않는 요청 부분(폼 매개변수, HTTP 헤더 또는 기타 부분) 중 하나에 포함되어야 함

HTMP Forms

  • HTML 폼을 서버에 제출하려면 CSRF 토큰을 hidden 값으로 Form에 포함해야 함
<form action="/memberJoin" method="post">
<input type="hidden" name="${_csrf.parameterName}" value="${_csrf.token}"/>
</form>
<input type="hidden" name="_csrf" value="4bfd1575-3ad1-4d21-96c7-4ef2d9f86721"/>
  • 폼에 실제 CSRF 토큰을 자동으로 포함하는 뷰는 다음과 같음
    → 위의 코드처럼 굳이 input hidden으로 토큰 값을 저장하지 않아도 됨
    • Thymeleaf
    • Spring 의 폼 태그 라이브러리 → <%@ taglib prefix="form" uri="http://www.springframework.org/tags/form" %>

JavaScript Applications

  • Single Page Application
    1. CookieCsrfTokenRepository.withHttpOnlyFalse를 사용해서 클라이언트가 서버가 발행한 쿠키로 부터 CSRF 토큰을 읽을 수 있도록 함
    2. 사용자 정의 CsrfTokenRequestHandler를 만들어 클라이언트가 요청 헤더나 요청 파라미터로 CSRF 토큰을 제출할 경우 이를 검증하도록 구현
    3. 클라이언트의 요청에 대해 CSRF 토큰을 쿠키에 렌더링해서 응답할 수 있도록 필터를 구현


Multi Page Application

  • JavaScript 가 각 페이지에서 로드되는 멀티 페이지 애플리케이션의 경우 CSRF 토큰을 쿠키에 노출시키는 대신 HTML 메타 태그 내에 CSRF 토큰을 포함시킬 수 있음
  • HTML 메타 태그에 CSRF 토큰 포함
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<meta name="_csrf" th:content="${_csrf.token}"/>
<meta name="_csrf_header" th:content="${_csrf.headerName}"/>
</html>
  • AJAX 요청에서 CSRF 토큰 포함
function login() {
const csrfHeader = $('meta[name="_csrf_header"]').attr('content');
const csrfToken = $('meta[name="_csrf"]').attr('content')
fetch('/api/login', {
	method: 'POST',
	headers: {[csrfHeader]: csrfToken }
})

추가 정리

  • CsrfTokenRequestAttributeHandler
    • 보통 CSRF 토큰을 인코딩하거나 복호화 않고 요청과 세션에서 직접 비교할 때 사용
    • 주로 CSRF 토큰이 평문으로 전달되고 이를 직접 비교하는 방식
  • XorCsrfTokenRequestAttributeHandler
    • CSRF 토큰을 XOR 연산을 통해 인코딩하여 전달하고, 이를 복호화하여 세션의 정보와 비교할 때 사용
    • CSRF 토큰을 평문으로 전달하지 않기 때문에 약간의 보안 이점을 제공
728x90