Cross Site Request Forgery로 대충 직역하면 교차 사이트 요청 변조이다.
대충 사이트에서 요청을 변조한다는 의미로서 인증된 유저의 계정을 이용해 악의적인 변경요청을 만들어 이익을 취하는 공격방법이다.
서버는 사용자가 로그인을 하면 사용자의 정보를 세션에 저장하고 클라이언트는 세션ID를 쿠키에 저장한다. 서버는 쿠키에 담긴 세션ID를 통해 사용자의 인증여부를 파악한다.
stateless한 restAPI에서는 쿠키나 세션을 사용하지 않기때문에 csrf보호가 따로 필요가 없기 때문에 disable로 처리한다.
Referrer 검증
Spring Security CSRF Token 사용
스프링 시큐리티에서는 @EnableWebSecurity 어노테이션이 기본적으로 CSRF 공격을 방지하는 기능을 지원함
스프링 시큐리티에서 CsrfFilter가 구현되어 있다.
CsrfFilter 클래스
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
throws ServletException, IOException {
// 1. 저장되어 있는 csrf token확인
request.setAttribute(HttpServletResponse.class.getName(), response);
CsrfToken csrfToken = this.tokenRepository.loadToken(request);
boolean missingToken = (csrfToken == null);
if (missingToken) {
// 2. 서버에서 csrf token 생성
csrfToken = this.tokenRepository.generateToken(request);
this.tokenRepository.saveToken(csrfToken, request, response);
}
request.setAttribute(CsrfToken.class.getName(), csrfToken);
request.setAttribute(csrfToken.getParameterName(), csrfToken);
// 3. csrf filter 사용 유무 확인
if (!this.requireCsrfProtectionMatcher.matches(request)) {
if (this.logger.isTraceEnabled()) {
this.logger.trace("Did not protect against CSRF since request did not match "
+ this.requireCsrfProtectionMatcher);
}
filterChain.doFilter(request, response);
return;
}
// 4. request로 부터 csrf token 추출
String actualToken = request.getHeader(csrfToken.getHeaderName());
if (actualToken == null) {
actualToken = request.getParameter(csrfToken.getParameterName());
}
// 5. 서버에서 생성한 csrf token과 request로 받은 csrf token 비교
if (!equalsConstantTime(csrfToken.getToken(), actualToken)) {
this.logger.debug(
LogMessage.of(() -> "Invalid CSRF token found for " + UrlUtils.buildFullRequestUrl(request)));
AccessDeniedException exception = (!missingToken) ? new InvalidCsrfTokenException(csrfToken, actualToken)
: new MissingCsrfTokenException(actualToken);
this.accessDeniedHandler.handle(request, response, exception);
return;
}
filterChain.doFilter(request, response);
}
@EnableWebSecurity을 사용한다면 csrf공격을 방지할 수 있음
HttpOnly가 체크된 쿠키는 자바스크립트로 컨트롤 할 수 없기 때문에 .csrf().csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse())를 명시해주어야 함
public static CookieCsrfTokenRepository withHttpOnlyFalse() {
CookieCsrfTokenRepository result = new CookieCsrfTokenRepository();
result.setCookieHttpOnly(false);
return result;
}
public final class CookieCsrfTokenRepository implements CsrfTokenRepository {
static final String DEFAULT_CSRF_COOKIE_NAME = "XSRF-TOKEN";
static final String DEFAULT_CSRF_PARAMETER_NAME = "_csrf";
static final String DEFAULT_CSRF_HEADER_NAME = "X-XSRF-TOKEN";