CSRF(사이트 간 요청 위조, Cross-site request forgery)
공격자가 인증된 브라우저에 저장된 쿠키의 세션 정보를 활용하여 웹 서버에 사용자가 의도하지 않는 요청을 전달하는 것
예를 들어, 피해자의 이메일 주소, 비밀 번호를 변경하거나 자금이체를 하는 등의 동작을 수행하게 할 수 있습니다.
CSRF 동작 원리
CSRF가 성공하려면, 아래 3가지 조건이 만족되어야 합니다.
1. 사용자는 보안이 취약한 서버로부터 이미 로그인 되어 있는 상태
2. 쿠기 기반의 서버 세션 정보를 획득
3. 공격자는 서버를 공격하기 위한 요청 방법에 대해 미리 파악
해당 조건이 만족되면 다음의 과정을 거쳐 CSRF 공격이 이루어지게 됩니다.
1. 사용자가 서버에 로그인
2. 서버에 저장된 세션 정보를 사용할 수 있는 SessionID가 사용자의 브라우저 쿠키에 저장
3. 공격자는 사용자가 악성 스크립트 페이지를 누르도록 유도
4. 사용자가 악성 스크립트가 작성된 페이지 접근 시, 웹 브라우저에 의해 쿠키가 저장된 SessionID와 함께 서버로 요청
5. 서버는 쿠키에 담긴 SessionID를 보고 해당 요청이 인증된 사용자로부터 온 것으로 판단하고 처리
CSRF 방어 방법
1. Referer Check (리퍼러 체크)
HTTP 요청 헤더(request header) 정보에서 Referrer 정보를 확인할 수 있습니다. 보통이라면 호스트(host)와 Referrer 값이 일치하므로 둘을 비교합니다. CSRF 공격의 대부분 Referrer 값에 대한 검증만으로도 많은 수의 공격을 방어할 수 있다고 합니다.
public class ReferrerCheck implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
String referer = request.getHeader("Referer");
String host = request.getHeader("host");
if (referer == null || !referer.contains(host)) {
response.sendRedirect("/");
return false;
}
return true;
}
}
출처: https://devscb.tistory.com/123 [SCB개발자이야기:티스토리]
2. CAPTCHA 도입
요청 시에 CAPTCHA를 이용하여, CAPTHCA 인증코드가 없거나 틀리면 요청을 거부하도록 할 수 있습니다.
3. CSRF 토큰 사용
사용자 세션에 임의에 값을 저장하여 모든 요청마다 해당 값을 포함하여 전송하도록 합니다. 서버에서 요청을 받을 때 마다, 세션에 저장된 값과 요청으로 전송된 값이 일치하는지 검증하여 방어하는 방법입니다.
//로그인시 session value에 CSRF_TOKEN값 저장
session.setAttribute("CSRF_TOKEN", UUID.randomUUID().toString());
출처: https://devscb.tistory.com/123 [SCB개발자이야기:티스토리]
<!---요청시 CSRF_TOKEN 값을 전송하도록 해줌---->
<form action="http://example/path" method="POST">
<input type="hidden" name="CSRF_TOKEN" value="${CSRF_TOKEN}">
<!-- ... -->
</form>
출처: https://devscb.tistory.com/123 [SCB개발자이야기:티스토리]
// 요청을 받을시, 인터셉터에서 CSRF_TOKEN값 검증하도록 함.
public class CsrfTokenInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
HttpSession httpSession = request.getSession();
String csrfTokenParam = request.getParameter("CSRF_TOKEN");
String csrfTokenSession = (String) httpSession.getAttribute("CSRF_TOKEN");
if (csrfTokenParam == null || !csrfTokenParam.equals(csrfTokenSession)) {
response.sendRedirect("/");
return false;
}
return true;
}
}
출처: https://devscb.tistory.com/123 [SCB개발자이야기:티스토리]
CSRF를 disable 하는 경우
CSRF를 disable 해도 되는 경우는 극히 제한적이며, 일반적으로 권장되지 않습니다. 그러나 특정 상황에서는 예외가 있을 수 있습니다.
- 클라이언트 기반 인증 방식(토큰 등)을 사용하는 경우는 각 요청마다 인증
-> 공격자가 쿠키에 존재하는 JWT를 탈취하여 서버에 공격할 수 있지 않나? - 브라우저가 아닌 애플리케이션의 클라이언트가 이용하는 경우에는 CSRF를 비활성화