백엔드

CSRF와 XSS 방어

지식소 채움이 2025. 9. 18. 08:20

1. CSRF(Cross-Site Request Forgery)

1-1. 개념

CSRF는 사용자가 의도하지 않은 요청을 특정 웹 서비스로 보내게 만드는 공격입니다. 예를 들어, 사용자가 이미 은행 사이트에 로그인한 상태에서 악의적인 사이트를 방문했을 때, 공격자가 준비한 폼이나 스크립트를 통해 자동으로 송금 요청이 전송될 수 있습니다. 서버는 세션 쿠키를 기반으로 해당 요청을 합법적인 사용자 요청으로 오인하기 때문에 문제가 발생합니다.

1-2. 공격 시나리오

  1. 사용자가 bank.com에 로그인 → 세션 쿠키 발급
  2. 공격자가 evil.com에 악성 HTML 폼 배치
  3. 사용자가 evil.com을 방문 → 브라우저가 자동으로 bank.com/transfer 요청 전송 (쿠키 포함)
  4. 서버는 사용자가 보낸 정상 요청처럼 처리 → 계좌 이체 성공

1-3. 방어 방법

  • CSRF 토큰 사용: 요청마다 서버가 난수 토큰을 발급하고, 클라이언트가 폼 전송 시 반드시 포함하도록 합니다. 서버는 토큰 일치 여부를 검증해 공격을 차단합니다.
  • SameSite 쿠키 설정: 세션 쿠키를 SameSite=Lax 또는 Strict로 설정하여 다른 도메인에서 자동 전송되지 않도록 합니다.
  • 중요 요청은 GET 대신 POST 사용: GET 요청은 브라우저가 예측하지 못한 상황에서 자동 실행될 수 있으므로, 데이터 변경은 반드시 POST/PUT/DELETE로 제한합니다.

1-4. Django에서의 구현

Django는 기본적으로 CSRF 보호를 내장하고 있습니다. 템플릿 내 폼에 {% csrf_token %}을 추가하면 자동으로 토큰이 삽입됩니다.

<form method="post" action="/transfer/">
  {% csrf_token %}
  <input type="text" name="to_account">
  <input type="number" name="amount">
  <button type="submit">송금</button>
</form>

뷰에서 @csrf_protect 데코레이터 또는 CsrfViewMiddleware를 통해 토큰 검증이 자동 수행됩니다.
API 서버의 경우, React/Vue 같은 프런트엔드와 연동 시 CSRF 토큰을 헤더에 포함해야 하며, Django REST Framework에서는 X-CSRFToken 헤더를 기본적으로 지원합니다.


2. XSS(Cross-Site Scripting)

2-1. 개념

XSS는 공격자가 악성 스크립트를 사용자 브라우저에서 실행하게 하는 공격입니다. 사용자가 입력한 값이 적절히 필터링되지 않고 HTML에 삽입될 때 발생합니다. 이를 통해 공격자는 세션 탈취, 키로깅, 피싱 등 다양한 공격을 수행할 수 있습니다.

2-2. 공격 유형

  • 저장형 XSS(Persistent): 악성 스크립트가 DB에 저장된 뒤 여러 사용자에게 노출됩니다. (예: 게시판 댓글에 <script> alert('해킹')</script> 삽입)
  • 반사형 XSS(Reflected): URL 파라미터에 포함된 스크립트가 그대로 응답에 반영됩니다. (예: /search? q=<script>...)
  • DOM 기반 XSS: 클라이언트 측 자바스크립트 코드에서 입력값을 검증 없이 DOM에 삽입할 때 발생합니다.

2-3. 방어 방법

  • 출력 인코딩(Escape): HTML에 사용자 입력을 출력할 때 반드시 이스케이프 처리합니다. (< → &lt;)
  • 입력 검증: 예상치 못한 태그, 이벤트 핸들러(onerror, onclick) 등을 필터링합니다.
  • Content Security Policy(CSP): script-src 'self' 같은 CSP 헤더를 설정하여 외부 스크립트 실행을 제한합니다.
  • 쿠키에 HttpOnly 설정: 세션 쿠키를 자바스크립트에서 접근할 수 없도록 차단합니다.

2-4. Django에서의 구현

Django 템플릿 엔진은 기본적으로 변수 출력 시 HTML 이스케이프 처리를 수행합니다.

<p>{{ comment }}</p>

만약 공격자가 <script> alert('XSS')</script>를 입력하더라도 브라우저에는 &lt;script&gt;...&lt;/script&gt;로 표시됩니다.

단, |safe 필터를 사용할 경우 이스케이프가 비활성화되므로, 반드시 신뢰할 수 있는 데이터에만 적용해야 합니다.

추가적으로 CSP를 적용하려면 Django 미들웨어에서 헤더를 설정할 수 있습니다.

def security_headers(get_response):
    def middleware(request):
        response = get_response(request)
        response['Content-Security-Policy'] = "default-src 'self'"
        response['X-Content-Type-Options'] = "nosniff"
        return response
    return middleware

3. 정리

  • CSRF는 사용자의 세션을 악용해 의도하지 않은 요청을 보내는 공격 → 방어: CSRF 토큰, SameSite 쿠키, POST 사용
  • XSS는 악성 스크립트를 브라우저에서 실행시키는 공격 → 방어: 출력 인코딩, 입력 검증, CSP, HttpOnly 쿠키

실무에서는 보안이 “한 번 막고 끝”이 아니라, **여러 방어 기법을 조합해 다층 방어(Defense in Depth)**를 구축해야 합니다.
예를 들어, Django의 기본 CSRF 보호를 사용하면서, 동시에 SameSite 쿠키를 설정하고, XSS 방지를 위해 CSP와 출력 이스케이프를 병행하는 식입니다.

웹 애플리케이션은 사용자 데이터와 신뢰를 기반으로 운영됩니다. CSRF와 XSS에 대한 이해와 방어는 개발자의 기본 역량이며, 이를 바탕으로 안전하고 신뢰할 수 있는 서비스를 제공할 수 있습니다.