CSRF 공격과 방어 전략: 웹 보안의 핵심 이해하기

2025. 6. 23. 09:52·Backend Development
반응형

웹 개발을 하다 보면 반드시 마주치게 되는 보안 이슈 중 하나가 바로 CSRF(Cross-Site Request Forgery) 공격입니다. 특히 백엔드 개발자라면 이 공격 방식과 방어 전략을 정확히 이해하고 있어야 안전한 웹 애플리케이션을 만들 수 있습니다.


CSRF 공격이란 무엇인가?

사이트 간 요청 위조(Cross-Site Request Forgery, CSRF) 공격은 사용자가 자신의 의지와 무관하게 공격자가 의도한 행위를 특정 웹사이트에 요청하도록 하는 공격 방식입니다.

쉽게 말해, 마치 은행에서 본인인척 하면서 다른 사람이 여러분의 계좌에서 돈을 인출하는 것과 같은 원리입니다. 사용자는 자신이 로그인한 상태를 악용당하게 되는 것이죠.


CSRF 공격의 동작 원리

1단계: 정상적인 로그인 과정

사용자 → [로그인 요청] → 웹사이트
사용자 ← [세션 쿠키 설정] ← 웹사이트

사용자가 웹사이트(예: 온라인 쇼핑몰)에 로그인하면, 서버는 세션 ID를 쿠키로 설정합니다. 이후 모든 요청에는 이 쿠키가 자동으로 포함됩니다.

2단계: 공격자의 함정

공격자는 다음과 같은 방법으로 사용자를 악성 페이지로 유도합니다:

  • 악성 스크립트가 포함된 이메일 발송
  • SNS나 게시판에 악성 링크 게시
  • 광고나 팝업을 통한 유도

3단계: 악성 요청 실행

사용자가 악성 페이지에 접속하면, 다음과 같은 숨겨진 코드가 실행됩니다:

<!-- 공격자가 심어놓은 악성 코드 예시 -->
<img src="https://shopping-mall.com/api/purchase?item=expensive-laptop&quantity=10" 
     style="display:none;" />

<form action="https://banking-site.com/transfer" method="POST" style="display:none;">
  <input type="hidden" name="to" value="attacker-account" />
  <input type="hidden" name="amount" value="1000000" />
  <script>document.forms[0].submit();</script>
</form>

4단계: 피해 발생

브라우저는 자동으로 해당 사이트의 쿠키를 포함하여 요청을 전송하므로, 서버 입장에서는 정상적인 사용자의 요청으로 인식하게 됩니다.


실제 공격 시나리오 예시

시나리오: 온라인 뱅킹 공격

  1. 김개발자가 A은행 사이트에 로그인 (세션 쿠키 저장됨)
  2. 로그인 상태를 유지한 채 이메일을 확인
  3. "무료 쿠폰 받기" 링크를 클릭 (실제로는 공격자 사이트)
  4. 공격자 사이트에 숨겨진 코드가 실행:
    <iframe src="https://a-bank.com/transfer?to=attacker&amount=100000" 
            style="display:none;"></iframe>
  5. A은행 서버는 정상적인 이체 요청으로 인식하여 처리
  6. 김개발자는 자신도 모르는 사이에 돈을 이체하게 됨

CSRF 공격 방어 전략

1. Referer 헤더 검증

// Node.js Express 예시
app.post('/api/transfer', (req, res) => {
  const referer = req.get('Referer');
  const host = req.get('Host');

  if (!referer || !referer.includes(host)) {
    return res.status(403).json({ error: 'Invalid request origin' });
  }

  // 정상 처리 로직
});

장점: 간단한 구현
단점: Referer 헤더는 조작이 가능하고, 일부 브라우저에서 전송되지 않을 수 있음

2. CSRF 토큰 (가장 일반적인 방법)

// 토큰 생성 (서버 측)
const crypto = require('crypto');

function generateCSRFToken(session) {
  const token = crypto.randomBytes(32).toString('hex');
  session.csrfToken = token;
  return token;
}

// HTML 폼에 토큰 포함
app.get('/transfer-form', (req, res) => {
  const csrfToken = generateCSRFToken(req.session);
  res.render('transfer', { csrfToken });
});
<!-- 클라이언트 측 폼 -->
<form action="/api/transfer" method="POST">
  <input type="hidden" name="csrf_token" value="{{ csrfToken }}" />
  <input type="text" name="to" placeholder="받는 사람" />
  <input type="number" name="amount" placeholder="금액" />
  <button type="submit">이체하기</button>
</form>
// 토큰 검증 (서버 측)
app.post('/api/transfer', (req, res) => {
  const { csrf_token } = req.body;

  if (!csrf_token || csrf_token !== req.session.csrfToken) {
    return res.status(403).json({ error: 'Invalid CSRF token' });
  }

  // 정상 처리 로직
});

3. SameSite 쿠키 설정

// Express에서 SameSite 쿠키 설정
app.use(session({
  cookie: {
    sameSite: 'strict', // 또는 'lax'
    secure: true, // HTTPS 환경에서만
    httpOnly: true
  }
}));
SameSite 옵션 설명 사용 시나리오
Strict 크로스 사이트 요청에서 쿠키 전송 완전 차단 높은 보안이 필요한 경우
Lax GET 요청 등 일부 크로스 사이트 요청만 허용 사용성과 보안의 균형
None 모든 크로스 사이트 요청에서 쿠키 전송 허용 외부 서비스 연동 시

4. CORS 정책 활용

// CORS 설정으로 허용된 도메인만 접근 가능
const cors = require('cors');

app.use(cors({
  origin: ['https://trusted-domain.com', 'https://my-app.com'],
  credentials: true
}));

개발 시 체크리스트

✅ 필수 적용 사항

  • 모든 상태 변경 API에 CSRF 토큰 적용
  • SameSite 쿠키 설정 적용
  • 민감한 작업에 대한 추가 인증 단계 구현
  • CORS 정책 적절히 설정

✅ 추가 보안 강화

  • 중요한 작업 시 비밀번호 재입력 요구
  • IP 주소 기반 접근 제한
  • 비정상적인 요청 패턴 모니터링
  • 보안 헤더 설정 (X-Frame-Options, Content-Security-Policy 등)

마무리

CSRF 공격은 사용자의 신뢰를 악용하는 교묘한 공격 방식입니다. 특히 온라인 쇼핑, 뱅킹, 소셜미디어 등 사용자 인증이 중요한 서비스에서는 반드시 방어 메커니즘을 구현해야 합니다.

핵심 포인트:

  • CSRF 공격은 사용자가 모르는 사이에 발생하는 공격
  • CSRF 토큰을 사용한 방어가 가장 일반적이고 효과적
  • SameSite 쿠키와 CORS 정책을 적절히 조합하여 다층 방어 구현
  • 중요한 작업에는 추가 인증 단계 도입 고려

웹 보안은 한 번 설정하고 끝나는 것이 아닙니다. 지속적인 모니터링과 업데이트를 통해 안전한 웹 애플리케이션을 유지하시기 바랍니다.


참고 자료

  • OWASP CSRF Prevention Cheat Sheet
  • MDN Web Docs - SameSite cookies
  • RFC 7034 - HTTP Header Field X-Frame-Options
반응형
저작자표시 비영리 변경금지 (새창열림)

'Backend Development' 카테고리의 다른 글

시스템 콜(System Call)이란? 운영체제와 프로그램 간의 소통 창구 완전 정복  (2) 2025.06.11
Redis의 싱글 스레드 아키텍처: 왜 빠른가?  (2) 2025.05.30
시스템 간 비동기 연동 방식 완벽 가이드  (0) 2025.05.30
프로세스 vs 스레드: 컨텍스트 스위칭의 차이점과 성능 비교  (0) 2025.05.30
데이터베이스의 신뢰성을 책임지는 ACID: 원자성, 일관성, 격리성, 지속성 완벽 가이드  (2) 2025.05.30
'Backend Development' 카테고리의 다른 글
  • 시스템 콜(System Call)이란? 운영체제와 프로그램 간의 소통 창구 완전 정복
  • Redis의 싱글 스레드 아키텍처: 왜 빠른가?
  • 시스템 간 비동기 연동 방식 완벽 가이드
  • 프로세스 vs 스레드: 컨텍스트 스위칭의 차이점과 성능 비교
Kun Woo Kim
Kun Woo Kim
안녕하세요, 김건우입니다! 웹과 앱 개발에 열정적인 전문가로, React, TypeScript, Next.js, Node.js, Express, Flutter 등을 활용한 프로젝트를 다룹니다. 제 블로그에서는 개발 여정, 기술 분석, 실용적 코딩 팁을 공유합니다. 창의적인 솔루션을 실제로 적용하는 과정의 통찰도 나눌 예정이니, 궁금한 점이나 상담은 언제든 환영합니다.
  • Kun Woo Kim
    WhiteMouseDev
    김건우
  • 깃허브
    포트폴리오
    velog
  • 전체
    오늘
    어제
  • 공지사항

    • [인사말] 이제 티스토리에서도 만나요! WhiteMouse⋯
  • 블로그 메뉴

    • 홈
    • 태그
    • 방명록
    • 분류 전체보기 (99) N
      • Frontend Development (38)
      • Backend Development (21) N
      • Algorithm (33) N
        • 백준 (11) N
        • 프로그래머스 (17)
        • 알고리즘 (5)
      • Infra (1)
      • 자료구조 (3)
  • 링크

    • Github
    • Portfolio
    • Velog
  • 인기 글

  • 태그

    frontend development
    tailwindcss
  • 최근 댓글

  • 최근 글

  • hELLO· Designed By정상우.v4.10.3
Kun Woo Kim
CSRF 공격과 방어 전략: 웹 보안의 핵심 이해하기
상단으로

티스토리툴바