본문 바로가기
기술의기록

5분만에 이해하는 CSRF 공격 완벽 가이드

by Jeremy Winchester 2025. 10. 4.
반응형

웹사이트를 운영하거나 개발하시는 분이라면 한 번쯤 'CSRF'라는 용어를 들어보셨을 거예요. "도대체 CSRF가 뭐길래 이렇게 중요하다고 하는 걸까?" 하는 의문을 가지셨다면, 지금부터 제가 친절하게 설명해드릴게요! 😊

오늘은 웹 보안의 핵심 개념 중 하나인 CSRF(Cross-Site Request Forgery, 사이트 간 요청 위조) 공격에 대해 알아보고, 실제로 어떻게 방어할 수 있는지까지 완벽하게 정리해드리겠습니다.


🤔 CSRF 공격이란? 쉽게 이해하기

CSRF는 한마디로 **"사용자가 의도하지 않은 요청을 브라우저가 자동으로 실행하게 만드는 공격"**입니다.

여러분이 은행 사이트에 로그인한 상태에서, 공격자가 만든 악성 사이트를 방문하게 되면 어떤 일이 벌어질까요? 놀랍게도 여러분의 은행 계좌에서 공격자의 계좌로 돈이 이체될 수 있습니다! 😱

가장 충격적인 건, 여러분은 아무 것도 하지 않았는데 이런 일이 발생한다는 거예요.

실제 사례로 보는 CSRF의 위험성

2008년 한국에서 발생했던 옥션 개인정보 유출 사건을 기억하시나요? 당시 1,863만 명의 회원 정보가 유출되었는데, 이 사건에서도 CSRF 공격 기법이 사용되었습니다. 공격자는 CSRF를 이용해 관리자 계정을 탈취했고, 결과적으로 막대한 피해가 발생했죠.

이처럼 CSRF는 단순한 이론이 아니라, 실제로 막대한 피해를 일으킬 수 있는 심각한 보안 위협입니다.


🎯 CSRF 공격은 어떻게 작동할까?

CSRF 공격의 메커니즘을 단계별로 살펴볼게요.

1단계: 사용자가 정상 사이트에 로그인

먼저 사용자가 A 서비스(예: 은행 사이트)에 로그인합니다. 서버는 세션을 유지하기 위해 브라우저에 쿠키를 저장합니다.

2단계: 악성 사이트 방문

사용자가 로그인한 상태로 다른 웹사이트, 특히 공격자가 만든 악성 사이트를 방문합니다. 이메일의 링크를 클릭하거나, SNS의 광고를 클릭하는 것만으로도 충분합니다.

3단계: 자동 요청 발생

악성 사이트에는 A 서비스로 향하는 폼 전송이나 스크립트가 숨어있습니다. 사용자가 버튼을 누르거나 심지어 페이지를 열기만 해도, 브라우저는 자동으로 A 서비스의 쿠키를 함께 첨부하여 요청을 보냅니다.

4단계: 서버의 오인

A 서비스 입장에서는 "쿠키가 붙어 있으니 정상 사용자 요청"으로 오인하고, 계좌 이체나 비밀번호 변경 같은 크리티컬한 작업을 수행하게 됩니다.

핵심 포인트 ⚠️

중요한 점은 브라우저가 알아서 쿠키를 붙여주는 기본 동작 때문에, 사용자가 의도하지 않은 요청도 서버 입장에서는 정상적인 요청처럼 보인다는 거예요!


💡 CSRF 공격이 성공하기 위한 조건

모든 웹사이트가 CSRF 공격에 취약한 건 아니에요. 다음 조건들이 충족되어야 공격이 가능합니다:

1. 쿠키 기반 인증 사용

  • 서버가 세션 정보를 쿠키로 관리하는 경우

2. 사용자가 이미 인증된 상태

  • 로그인이 되어있고, 세션이 유효한 상태

3. 공격자가 요청 패턴을 파악

  • 비밀번호 변경, 계좌 이체 등의 API 엔드포인트를 알고 있는 경우

4. 악성 사이트에 접속 유도

  • 피싱 메일, SNS 광고, 악성 게시글 등을 통한 유입

이 조건들이 생각보다 쉽게 충족된다는 게 CSRF 공격의 무서운 점입니다! 😰


🛡️ CSRF 공격 방어 방법 완벽 정리

자, 이제 가장 중요한 부분입니다. CSRF 공격을 어떻게 막을 수 있을까요?

방법 1: CSRF 토큰 사용 (가장 강력한 방법!) ✨

CSRF 토큰은 서버가 생성한 랜덤한 문자열로, 각 사용자의 세션마다 고유하게 발급됩니다.

작동 원리

  1. 사용자가 페이지를 요청하면, 서버는 랜덤 토큰을 생성하여 세션에 저장
  2. 이 토큰을 폼의 hidden 필드나 헤더에 포함시켜 클라이언트에 전달
  3. 사용자가 폼을 제출할 때, 토큰도 함께 전송
  4. 서버는 받은 토큰과 세션에 저장된 토큰을 비교
  5. 일치하면 정상 처리, 불일치하면 403 에러로 차단

왜 효과적일까?

악성 사이트는 사용자의 쿠키는 알 수 있지만, 세션에 저장된 CSRF 토큰은 알 수 없습니다. 브라우저의 동일 출처 정책(SOP) 때문에 JavaScript로 다른 도메인의 토큰을 읽을 수 없기 때문이죠!

// 예시: 폼에 CSRF 토큰 포함하기
<form action="/transfer" method="POST">
  <input type="hidden" name="_csrf" value="랜덤토큰값">
  <input type="text" name="amount" value="10000">
  <button type="submit">이체하기</button>
</form>

방법 2: SameSite 쿠키 속성 설정 🍪

SameSite는 쿠키 속성 중 하나로, 크로스 사이트 요청 시 쿠키 전송을 제어합니다.

세 가지 옵션

  • Strict: 가장 강력한 보호. 크로스 사이트 요청 시 쿠키를 절대 전송하지 않음
  • Lax: 기본값. 안전한 GET 요청과 최상위 탐색에서만 쿠키 전송 허용
  • None: 모든 크로스 사이트 요청에 쿠키 전송 (Secure 속성 필수)
// 예시: SameSite 쿠키 설정
Set-Cookie: sessionId=abc123; SameSite=Strict; Secure; HttpOnly

실무 팁 💡

  • 은행 같은 보안이 중요한 사이트: Strict 사용
  • 일반 서비스: Lax가 보안과 사용성의 균형점
  • 2025년 현재, 대부분의 모던 브라우저가 SameSite를 지원합니다!

방법 3: Referer 헤더 검증 📋

HTTP 요청 헤더의 Referer 필드를 확인하여, 요청이 올바른 도메인에서 왔는지 검증하는 방법입니다.

장점

  • 구현이 간단하고 빠름
  • 소규모 웹사이트에 적합

단점

  • Referer는 조작이 가능하여 완벽한 방어가 어려움
  • 일부 브라우저나 보안 설정에서 Referer를 보내지 않을 수 있음
// 예시: Referer 검증 코드
String referer = request.getHeader("Referer");
String host = request.getHeader("host");

if (referer == null || !referer.contains(host)) {
    // 거부 처리
    response.sendRedirect("/error");
}

방법 4: GET/POST 메서드 구분 🔀

데이터를 변경하는 작업은 반드시 POST, PUT, DELETE 등의 메서드를 사용하고, GET은 조회 용도로만 사용합니다.

왜냐하면 <img> 태그나 <a> 태그를 통한 CSRF 공격은 대부분 GET 요청이기 때문이죠!

// ❌ 나쁜 예시 - GET으로 데이터 변경
app.get('/delete-account', (req, res) => {
  // 계정 삭제 로직
});

// ✅ 좋은 예시 - POST로 데이터 변경
app.post('/delete-account', csrfProtection, (req, res) => {
  // 계정 삭제 로직
});

방법 5: CAPTCHA 추가 인증 🤖

중요한 작업(비밀번호 변경, 계좌 이체 등)에는 CAPTCHA나 재인증을 요구하여 추가 보안층을 만듭니다.

사용자 경험(UX)은 다소 떨어질 수 있지만, 보안이 최우선인 경우 매우 효과적입니다!


⚙️ 프레임워크별 CSRF 방어 구현

대부분의 현대 웹 프레임워크는 CSRF 방어 기능을 기본으로 제공합니다!

Spring Security (Java) ☕

// 기본적으로 CSRF 보호 활성화됨
@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
            .csrf().csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse());
            // REST API의 경우 .csrf().disable() 가능
    }
}

Django (Python) 🐍

# settings.py에서 CSRF 미들웨어 활성화 (기본값)
MIDDLEWARE = [
    'django.middleware.csrf.CsrfViewMiddleware',
]

# 템플릿에서 사용
{% csrf_token %}

Express.js (Node.js) 🟢

const csrf = require('csurf');
const csrfProtection = csrf({ cookie: true });

app.post('/transfer', csrfProtection, (req, res) => {
    // 토큰 검증 후 처리
});

Laravel (PHP) 🐘

<!-- Blade 템플릿에서 자동으로 토큰 삽입 -->
<form method="POST" action="/transfer">
    @csrf
    <!-- 폼 내용 -->
</form>

🆚 CSRF vs XSS: 헷갈리는 두 공격의 차이점

많은 분들이 CSRF와 XSS를 혼동하시는데, 핵심 차이는 이거예요:

구분 CSRF XSS

공격 대상 웹 애플리케이션이 사용자를 신뢰 사용자가 웹사이트를 신뢰
악성 코드 실행 위치 서버 측 클라이언트(브라우저) 측
공격 목적 사용자 권한으로 의도하지 않은 작업 수행 사용자 정보 탈취, 악성 스크립트 실행
쿠키 사용 여부 쿠키를 악용 쿠키를 탈취

간단히 말해, **CSRF는 '나를 사칭한 요청'이고, XSS는 '악성 스크립트 주입'**입니다!


🚀 REST API는 CSRF로부터 안전할까?

여기서 중요한 질문! "REST API를 사용하면 CSRF 공격을 걱정하지 않아도 될까요?"

답은 "상황에 따라 다릅니다" ⚠️

안전한 경우:

  • JWT나 Bearer Token을 Authorization 헤더에 담아 사용하는 경우
  • 쿠키를 전혀 사용하지 않는 경우
  • localStorage나 sessionStorage에 토큰을 저장하고 JavaScript로 수동으로 헤더에 추가하는 경우

여전히 위험한 경우:

  • REST API임에도 세션 쿠키를 사용하는 경우
  • SameSite 설정이 None인 쿠키를 사용하는 경우

많은 개발자들이 "REST API니까 CSRF 보호를 비활성화해도 돼"라고 생각하지만, 쿠키를 사용한다면 여전히 위험합니다! 🚨


📱 2025년 트렌드: 모바일 앱도 조심해야!

최근에는 모바일 포렌식 도구를 이용한 CSRF 공격도 발견되고 있습니다.

2024년에는 Cellebrite라는 포렌식 회사가 개발한 도구로, 물리적 접근이 가능한 경우 Android 기기의 잠금을 해제하는 공격 체인이 발견되었죠. (CVE-2024-53104)

이처럼 CSRF의 공격 벡터는 계속 진화하고 있으며, 웹뿐만 아니라 모바일 환경에서도 보안에 신경 써야 합니다!


✅ 개발자를 위한 CSRF 방어 체크리스트

실무에서 바로 적용할 수 있는 체크리스트를 정리했어요! 📝

필수 사항:

  • [ ] 쿠키 기반 인증을 사용한다면 CSRF 토큰 구현
  • [ ] SameSite 쿠키 속성을 Lax 또는 Strict로 설정
  • [ ] 중요한 작업은 POST/PUT/DELETE 메서드로만 처리
  • [ ] 프레임워크의 기본 CSRF 보호 기능 활성화

권장 사항:

  • [ ] Referer 헤더 검증 추가
  • [ ] 민감한 작업에 재인증 또는 CAPTCHA 추가
  • [ ] CORS 정책 올바르게 설정
  • [ ] 정기적인 보안 감사 및 취약점 스캔

피해야 할 것:

  • [ ] GET 요청으로 데이터 변경 작업 수행 금지
  • [ ] REST API라고 무조건 CSRF 보호 비활성화 금지
  • [ ] 프레임워크 기본 보안 설정 임의 변경 금지

💭 마무리하며

오늘은 CSRF 공격의 개념부터 실제 방어 방법까지 완벽하게 정리해봤습니다!

처음에는 복잡해 보일 수 있지만, 핵심은 간단해요:

  • CSRF는 사용자 몰래 요청을 보내는 공격
  • CSRF 토큰과 SameSite 쿠키로 효과적으로 방어 가능
  • 프레임워크의 기본 보호 기능을 적극 활용하자
반응형