BackEnd/Spring

CSRF 공격을 방지하기 위한 SameSite 설정 - CSRF(1)

Potwings 2025. 9. 28. 17:15

인증과 관련하여 Spring Security에 대해 학습하던 중 CSRF라는 키워드에 갑자기 궁금증이 생겼다.

 

최근에는 대부분의 백엔드 서버가 REST API를 활용하여 데이터를 제공하고 있어 CSRF에 대한 검증을 하지 않는다 하였다.

REST API 서버에서 CSRF에 대한 검증이 disable 상태인 모습

 

그렇다면 기존의 세션 방식으로 인증을 진행할 때는 왜 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 공격을 예방할 수 있다.

 

 

SameSite Lax 설정 추가

 

SameSite가 Lax로 설정된 모습

 

인증 정보 쿠키가 없으므로 글이 등록되지 않고 login 페이지로 redirect 되는 것을 확인할 수 있다.

 

추가적으로 다음 글에서는 내부에서 발생한 CSRF(+XSS) 공격을 예방하는 방법을 알아보도록 하겠다.

반응형