CS

CSRF

k-oyun 2025. 4. 8. 02:03

CSRF란

CSRF(Cross-Site request Forgery, 사이트 간 요청 위조)는 웹 보안 취약점 중 하나로, 사용자가 의도하지 않은 요청을 공격자에 의해 대신 보내게 하는 공격 방식이다. 사용자가 로그인 상태일 때 공격자가 의도한 요청을 사용자 계정으로 서버에 전송하게 만들 수 있어 위험하다.

 

예시

 

1. 사용자가 https://bank.com에 로그인했다고 가정하자.

  • 로그인 후 세션 쿠키가 브라우저에 저장된다.
Set-Cookie: sessionid=abc123; Secure; HttpOnly; SameSite=None

 

2. 사용자가 https://evil.com에 방문 (악성 사이트)

  • 악성 사이트에는 아래와 같은 자동 전송 요청 코드가 있다.
<!-- 악성 사이트: evil.com -->
<!DOCTYPE html>
<html>
  <body>
    <h1>Free iPhone! Click here!</h1>

    <!-- 자동으로 bank.com에 요청 전송 -->
    <img src="https://bank.com/transfer?to=hacker&amount=1000" />
    
    <!-- 또는 form + JS 자동 submit -->
    <form id="stealMoney" action="https://bank.com/transfer" method="POST">
      <input type="hidden" name="to" value="hacker123">
      <input type="hidden" name="amount" value="1000">
    </form>

    <script>
      document.getElementById('stealMoney').submit();
    </script>
  </body>
</html>

 

3. 브라우저는 요청에 자동으로 사용자의 세션 쿠키를 포함시킨다.

 

POST /transfer HTTP/1.1
Host: bank.com
Cookie: sessionid=abc123
Content-Type: application/x-www-form-urlencoded

to=hacker123&amount=1000

 

4. 서버는 피해자의 세션 쿠키가 유효하므로 정상 요청으로 처리하게 된다.

  • 결과적으로 사용자가 의도하지 않았지만 공격자가 원하는 행동이 실행된다.(CSRF)
  • 예) 의도치 않게 사용자 계정을 삭제시키거나 계좌의 돈을 공격자의 계좌로 입금시킬 수 있다.

 

왜 위험할까?

  • 사용자는 아무것도 클릭하지 않아도 의도치 않게 요청된다.
  • img, form, iframe, fetch 등 다양한 방식으로 요청이 가능하다.
  • 로그인 세션이 유지된 상태라면 모든 인증된 기능이 공격 대상이 된다.

 

CSRF의 특징

  • 사용자의 세션 쿠키를 이용한다. (Http 요청에는 쿠키가 모두 자동으로 포함)
  • 주로 GET/POST 요청을 통해 실행된다.
  • 서버 입장에서는 정상 사용자의 요청처럼 보인다.

 

프레임워크에서의 CSRF 대응

  • Django: 기본적으로 CSRF 토큰을 사용하며, 미들웨어에서 자동으로 검증한다.
  • Spring Security: CSRF 보호 기능이 기본으로 활성화되어 있다.

 

CSRF 방어법

CSRF Token 사용 (가장 보편적이자 강력한 방법)

    • 서버가 매 요청 또는 세션마다 랜덤 토큰을 발급한다.
    • 클라이언트는 폼이나 요청 헤더에 이 토큰을 포함해서 전송한다.
    • 서버는 이 토큰이 세션에 저장된 값과 일치하는지 검증 후 요청을 수행한다.

Html Form 예시

<form method="POST" action="/transfer">
  <input type="hidden" name="csrf_token" value="3f7x9x2c9" />
</form>

 

React + axios 예시

import axios from 'axios';

const api = axios.create({
  baseURL: 'https://api.yourdomain.com',
  withCredentials: true,
});

// CSRF 토큰 받아오기
async function initCSRF() {
  const res = await api.get('/csrf-token'); // 서버에서 토큰 발급
  const csrfToken = res.data.csrfToken;

  // 이후 모든 요청에 토큰 헤더 추가
  api.defaults.headers.common['X-CSRF-Token'] = csrfToken;
}

// 호출
initCSRF();

 

공격자의 위조된 요청이 아래와 같이 전송되어도 공격자는 CSRF 토큰을 알 수 없기 때문에 위조된 요청이 처리되지 않는다.

 

POST /transfer HTTP/1.1
Host: bank.com
Cookie: sessionid=abc123
Content-Type: application/x-www-form-urlencoded
CSRF 토큰 없음!!


to=hacker123&amount=1000

 

공격자는 왜 CSRF 토큰을 위조하지 못하는가?

1. CSRF 토큰은 세션마다 다르고, 랜덤 하게 생성된다.

  • 서버는 세션/쿠키 기준으로 개별 사용자에게 토큰을 발급한다.
  • 공격자는 사용자 세션의 토큰 값을 알 방법이 없다.

2. CSRF 토큰은 JavaScript에서 가져와야 한다.

  • 공격자는 악성 사이트에서 <script>로 사용자의 브라우저에서 fetch('/csrf-token')을 시도해도 CORS 정책 때문에 요청이 막히거나, 응답 본문을 읽지 못한다.

3. X-CSRF-Token은 JavaScript로 명시적으로 넣어야 한다.

  • HTML <form>이나 <img> 태그만으로는 헤더 조작이 불가능하다.
  • 헤더를 조작하려면 동일 출처에서 실행 중인 JavaScript가 필요하지만 공격자는 권한이 없다.

 

SameSite 쿠키 설정

  • SameSite 속성을 통해 쿠키가 외부 요청에 자동으로 붙지 않도록 제한한다.

Set-Cookie: sessionid=abc123; SameSite=Strict; Secure; HttpOnly

설정값 종류
Strict 완전 차단 (다른 사이트에서 요청해도 쿠키 전송X) ✅
Lax 일부 허용 (GET 같은 안전한 요청만 쿠키 전송) ⚠️
None 전부 허용 (단, Secure 필요) 위험할 수 있음 ❌

 

Referer / Origin  헤더 검증

  • 요청의 Origin 또는 Referer 헤더를 검사하여 요청이 신뢰된 출처에서 왔는지 확인한다.
  • 일부 브라우저/환경에서는 Referer이 안 들어오는 경우도 있기에 보조 수단으로 사용하는 게 좋다.

 

CORS 정책 설정

  • 백엔드 서버가 어떤 도메인에서 요청을 허용할지 명시한다.
  • 서버에서 허용한 도메인의 요청만 수락하여 외부 도메인의 위조된 요청을 수행하지 않는다.

 

중요 요청은 GET 대신 POST/PUT/DELETE로만

  • 대부분의 송금, 주문 같은 요청은 POST로 하는 게 기본이지만 다시 강조하자면 절대 GET으로 하면 안 된다.
  • 공격자는 img src(이미지 GET 요청)에 요청을 숨기는 경우가 있다. 따라서 중요 작업은 GET을 사용하지 않는다.

 

방어법 정리

방어 방법효과 설명
CSRF Token 강력 토큰 일치 여부로 위조 방지
SameSite 쿠키 효과적 외부 요청 시 쿠키 차단
Origin / Referer 검사 보조 수단 요청 출처 확인
CORS 정책 제한 보조 수단 API 호출 도메인 제한
POST 요청 강제 기초 방어 GET으로 민감 동작 금지