Computer Engineering/Django

Django에서 CSRF 공격을 막기 및 CSRF 토큰의 이해와 활용

jordan.bae 2023. 11. 8. 22:40

CSRF

웹 보안에 있어서 중요한 공격 유형 중 하나인 CSRF(Cross-Site Request Forgery)는 사용자의 의도와 무관하게 공격자가 준비한 악의적인 요청을 보내게 만드는 기법입니다. 사용자가 로그인 상태에서 공격자가 조작한 웹페이지에 접속할 경우, 그 페이지는 사용자가 인지하지 못하는 사이에 위조된 요청을 보내게 됩니다. 이를 통해 공격자는 사용자의 인증을 악용할 수 있습니다.
Django는 이러한 CSRF 공격을 방지하기 위한 강력한 메커니즘을 기본적으로 제공합니다. 그 핵심 중 하나는 'CSRF 토큰'이 있습니다. 
(또 다른 방법은 CORS와 관련된 설정으로 궁금하신 분은 이 포스팅을 보셔도 좋을 것 같습니다.)
 

CSRF 토큰이란?

CSRF 토큰은 클라이언트가 서버로 요청을 보낼 때마다 서버에 의해 생성되는 랜덤하고 예측 불가능한 문자열입니다. 이 토큰은 폼 데이터의 일부로서 클라이언트로부터 서버로 다시 전송되며, 서버는 이를 검증하여 요청의 유효성을 확인합니다.

 

Django에서의 CSRF 설정

Django 프로젝트에서는 MIDDLEWARE설정에 django.middleware.csrf.CsrfViewMiddleware가 기본적으로 포함되어 있어, CSRF 보호가 활성화됩니다. 이 미들웨어는 모든 POST, PUT, DELETE, PATCH 요청에 대해 CSRF 토큰의 존재와 올바름을 검증합니다.
 

Django Form

폼을 사용할 때는 `{% csrf_token %}` 템플릿 태그를 사용하여 자동으로 CSRF 토큰을 포함시킬 수 있습니다. 예를 들어, HTML 템플릿에 다음과 같은 코드를 작성하면 Django는 자동으로 CSRF 토큰을 생성하고, 해당 폼을 통해 서버로 전송될 때 검증합니다.

<form method="post">
    {% csrf_token %}
    <!-- 폼 필드들 -->
</form>


Javascript

Javascript에서 요청을 할 때는 cookie에 저장되어 있는 csrftoken을 꺼내다가 써야합니다.

브라우저 쿠키

function getCSRFToken() {
    return document.cookie.split('; ')
      .find(row => row.startsWith('csrftoken='))
      ?.split('=')[1];
}

function sendData(data) {
    fetch('/your-endpoint/', {
        method: 'POST',
        headers: {
            'Content-Type': 'application/json',
            // 서버가 인식할 수 있도록 헤더에 CSRF 토큰을 추가합니다.
            'X-CSRFToken': getCSRFToken()
        },
        body: JSON.stringify(data)
    })
    .then(response => response.json())
    .then(data => console.log(data))
    .catch((error) => console.error('Error:', error));
}


CSRF 토큰 예외 설정

특정 뷰에 대해 CSRF 검증이 필요 없는 경우, `@csrf_exempt` 데코레이터를 사용하여 CSRF 보호 기능을 비활성화할 수 있습니다. 특히, 웹 브라우저가 아닌 IOS, Android 같은 클라이언트에서 이용하는 API라고 하면 JWT같은 인증을 사용하고 해당 뷰는 csrf 인증을 제외하는게 보통입니다.
 

Decorator 사용하기

함수 기반 뷰에서 제외하기 위해서는 아래와 같이 decorator를 사용할 수 있습니다.

from django.views.decorators.csrf import csrf_exempt

@csrf_exempt
def my_view(request):
    # 여기에 로직 작성

 

DRF 사용하기

Django REST framework의 APIView 또는 @api_view 데코레이터를 사용하면, 기본적으로 CSRF 검증이 비활성화됩니다. 이는 REST framework가 이미 토큰 기반 인증을 전제로 하기 때문입니다.
 

Django 에서 CSRF 관련 설정

Django의 settings.py 파일에는 CSRF 보호를 위한 여러 설정이 있습니다.

  • CSRF_COOKIE_NAME: 이 설정은 CSRF 쿠키의 이름을 결정합니다. 기본값은 'csrftoken'입니다.
  • CSRF_COOKIE_AGE: 쿠키의 최대 수명을 초 단위로 설정합니다. 기본값은 31449600초(약 1년)입니다.
  • CSRF_COOKIE_DOMAIN CSRF 쿠키에 사용될 도메인을 설정합니다. 기본값은 None이며, 이 경우 브라우저는 쿠키를 설정한 도메인에만 쿠키를 보냅니다.
  • CSRF_COOKIE_SECURE: HTTPS를 통해서만 CSRF 쿠키를 전송할지 여부를 결정합니다. 기본값은 False입니다
  • CSRF_COOKIE_SAMESITE: crsftoken의 쿠키에 SAMESITE 정책에 관한 값으로 기본 값은 LAX입니다. 다른 호스트에서 해당 쿠키의 값을 쓰는 것을 막습니다.
  • CSRF_HEADER_NAME: AJAX 요청시 사용할 HTTP 헤더의 이름을 결정합니다. 기본값은 'HTTP_X_CSRFTOKEN'입니다
  • CSRF_TRUSTED_ORIGINS: host와 다른 origin에서 요청을 할 경우 사용하는 값으로, 서브 도메인에서 요청하는 경우 이 값에 도메인 ('.example.com') 또는 sub-domain ('https://front.example.com') 을 추가해서 서브도메인 요청을 허용할 수 있습니다. 즉, sub-domain에서 요청을 하는 경우 해당 값을 이 설정값의 리스트 안에 포함시켜야 합니다.

 

관련 추천 포스팅

- Django CORS 관련 설정하기 / django-cors-headers

 

Django CORS 관련 설정하기 / django-cors-headers

웹 개발을 하다보면 CORS(Cross-Origin Resource Sharing)와 같은 보안 이슈들이 흔히 발생합니다. 이는 브라우저가 서로 다른 호스트(도메인) 간의 자원 공유를 제한함으로써, 웹 애플리케이션의 보안을

blog.doosikbae.com

- Django DB Transaction

 

반응형