Django 애플리케이션에 내용 보안 정책으로 보안 설정하는 방법

저자는 걸스 후 코드를 기부 대상으로 선정하여 기부를 위해 쓰기 프로그램의 일환으로 기부했습니다.

소개

웹사이트를 방문할 때 다양한 자원이로드되고 렌더링됩니다. 예를 들어 https://www.digitalocean.com에 들어가면 브라우저가 HTML과 CSS를 직접 digitalocean.com에서 다운로드합니다. 그러나 이미지 및 기타 자산은 assets.digitalocean.com에서 다운로드되며 분석 스크립트는 각각의 도메인에서 로드됩니다.

일부 웹사이트는 콘텐츠를로드하고 렌더링하는 데 다양한 서비스, 스타일 및 스크립트를 사용하며 브라우저는 모두 실행합니다. 브라우저는 코드가 악의적인지 여부를 알지 못하기 때문에 사용자를 보호하는 것은 개발자의 책임입니다. 웹사이트에는 많은 리소스가 있을 수 있으므로 브라우저에 승인된 리소스만 허용하는 기능이 있는 것이 좋습니다. 이것이 콘텐츠 보안 정책 (CSP)의 목적입니다.

CSP 헤더를 사용하면 개발자는 특정 리소스가 실행되도록 명시적으로 허용하고 다른 모든 리소스를 방지할 수 있습니다. 대부분의 사이트는 100개 이상의 리소스를 가질 수 있으며 각 리소스는 특정 범주의 리소스에 대해 승인되어야 하므로 CSP를 구현하는 것은 번거로운 작업일 수 있습니다. 그러나 CSP가있는 웹사이트는 승인된 리소스 만 실행되도록 보장하기 때문에보다 안전합니다.

이 튜토리얼에서는 기본적인 Django 애플리케이션에서 CSP를 구현합니다. CSP를 사용하여 특정 도메인 및 인라인 리소스를 실행하도록 사용자 정의할 것입니다. 선택적으로 Sentry를 사용하여 위반 사항을 로깅할 수도 있습니다.

전제 조건

이 튜토리얼을 완료하려면 다음이 필요합니다:

단계 1 — 데모 뷰 만들기

이 단계에서는 응용 프로그램이 뷰를 처리하는 방식을 수정하여 CSP 지원을 추가할 것입니다.

전제 조건으로 Django를 설치하고 샘플 프로젝트를 설정했습니다. Django의 기본 뷰는 CSP 미들웨어의 모든 기능을 따라갈 수 없을 정도로 너무 간단합니다. 따라서 이 튜토리얼에서는 간단한 HTML 페이지를 만들 것입니다.

전제 조건에서 만든 프로젝트 폴더로 이동하십시오:

  1. cd django-apps

django-apps 디렉토리 안에 가상 환경을 만듭니다. 이를 일반적으로 env라고 부르지만, 프로젝트에 의미 있는 이름을 사용하세요.

  1. virtualenv env

이제 다음 명령을 사용하여 가상 환경을 활성화합니다:

  1. . env/bin/activate

가상 환경 내에서 views.py 파일을 프로젝트 폴더에 nano 또는 원하는 텍스트 편집기를 사용하여 만듭니다:

  1. nano django-apps/testsite/testsite/views.py

이제 다음을 views.py에 추가합니다:

django-apps/testsite/testsite/views.py
from django.shortcuts import render

def index(request):
    return render(request, "index.html")

작업이 끝나면 파일을 저장하고 닫습니다.

새로운 templates 디렉토리에 index.html 템플릿을 만듭니다:

mkdir django-apps/testsite/testsite/templates
nano django-apps/testsite/testsite/templates/index.html

index.html에 다음을 추가합니다:

django-apps/testsite/testsite/templates/index.html
<!DOCTYPE html>
<html>
    <head>
        <title>Hello world!</title>
        <link rel="preconnect" href="https://fonts.googleapis.com" />
        <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
        <link
            href="https://fonts.googleapis.com/css2?family=Yellowtail&display=swap"
            rel="stylesheet"
        />
        <style>
            h1 {
                font-family: "Yellowtail", cursive;
                margin: 0.5em 0 0 0;
                color: #0069ff;
                font-size: 4em;
                line-height: 0.6;
            }

            img {
                border-radius: 100%;
                border: 6px solid #0069ff;
            }

            .center {
                text-align: center;
                position: absolute;
                top: 50vh;
                left: 50vw;
                transform: translate(-50%, -50%);
            }
        </style>
    </head>
    <body>
        <div class="center">
            <img src="https://html.sammy-codes.com/images/small-profile.jpeg" />
            <h1>Hello, Sammy!</h1>
        </div>
    </body>
</html>

우리가 만든 뷰는 이 간단한 HTML 페이지를 렌더링할 것입니다. 이 페이지에는 Hello, Sammy! 텍스트와 Sammy the Shark의 이미지가 표시됩니다.

작업이 끝나면 파일을 저장하고 닫습니다.

이 뷰에 액세스하려면 urls.py를 업데이트해야 합니다:

  1. nano django-apps/testsite/testsite/urls.py

views.py 파일을 가져와 하이라이트된 줄을 추가하여 새 경로를 추가합니다:

django-apps/testsite/testsite/urls.py
from django.contrib import admin
from django.urls import path
from . import views

urlpatterns = [
    path('admin/', admin.site.urls),
    path('', views.index),
]

새로 만든 뷰는 이제 /를 방문할 때 볼 수 있습니다(애플리케이션이 실행 중인 경우).

파일을 저장하고 닫습니다.

마지막으로, settings.py에서 INSTALLED_APPS를 업데이트하여 testsite를 포함시켜야 합니다:

  1. nano django-apps/testsite/testsite/settings.py
django-apps/testsite/testsite/settings.py
# ...
INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'testsite',
]
# ...

여기서는 settings.pytestsite를 응용 프로그램 목록에 추가하여 Django가 프로젝트 구조에 대해 일부 가정을 할 수 있도록합니다. 이 경우, templates 폴더에 Django 템플릿이 있고 이를 사용하여 뷰를 렌더링할 수 있다고 가정합니다.

프로젝트의 루트 디렉토리(testsite)에서 다음 명령을 사용하여 Django 개발 서버를 시작하십시오. 서버의 IP 주소로 your-server-ip를 대체하십시오.

  1. cd ~/django-apps/testsite
  2. python manage.py runserver your-server-ip:8000

브라우저를 열고 your-server-ip:8000을 방문하십시오. 페이지는 다음과 유사해야합니다:

이 시점에서 페이지에는 Sammy the Shark의 프로필 사진이 표시됩니다. 이미지 아래에는 파란색 스크립트로 Hello, Sammy! 텍스트가 표시됩니다.

Django 개발 서버를 중지하려면 CONTROL-C를 누르십시오.

이 단계에서는 Django 프로젝트의 홈페이지로 작동하는 기본 뷰를 만들었습니다. 다음으로 응용 프로그램에 CSP 지원을 추가할 예정입니다.

단계 2 — CSP 미들웨어 설치

이 단계에서는 CSP 미들웨어를 설치하고 구현하여 뷰에서 CSP 헤더를 추가하고 CSP 기능을 사용할 수 있도록합니다. 미들웨어는 Django가 처리하는 모든 요청 또는 응답에 추가 기능을 제공합니다. 이 경우 Django-CSP 미들웨어가 Django 응답에 CSP 지원을 추가합니다.

먼저, 파이썬의 패키지 관리자인 pip를 사용하여 Django 프로젝트에 Mozilla의 CSP 미들웨어를 설치합니다. 다음 명령을 사용하여 PyPi, 파이썬 패키지 인덱스에서 필요한 패키지를 설치합니다. 명령을 실행하려면 Django 개발 서버를 CONTROL-C를 사용하여 중지하거나 터미널에서 새 탭을 엽니다:

  1. pip install django-csp

다음으로, Django 프로젝트의 설정에 미들웨어를 추가합니다. settings.py를 엽니다:

  1. nano testsite/testsite/settings.py

django-csp가 설치되었으므로 이제 settings.py에 미들웨어를 추가할 수 있습니다. 이렇게 하면 응답에 CSP 헤더가 추가됩니다.
MIDDLEWARE 구성 배열에 다음 줄을 추가하십시오:

testsite/testsite/settings.py
MIDDLEWARE = [
    'csp.middleware.CSPMiddleware',
    'django.middleware.security.SecurityMiddleware',
    'django.contrib.sessions.middleware.SessionMiddleware',
    'django.middleware.common.CommonMiddleware',
    'django.middleware.csrf.CsrfViewMiddleware',
    'django.contrib.auth.middleware.AuthenticationMiddleware',
    'django.contrib.messages.middleware.MessageMiddleware',
    'django.middleware.clickjacking.XFrameOptionsMiddleware',
]

작업이 완료되면 파일을 저장하고 닫으십시오. 이제 Django 프로젝트에서 CSP를 지원합니다. 다음 단계에서는 CSP 헤더를 추가하기 시작합니다.

단계 3 — CSP 헤더 구현

이제 프로젝트가 CSP를 지원하므로 보안을 강화할 준비가 되었습니다. 이를 위해 프로젝트를 구성하여 응답에 CSP 헤더를 추가할 것입니다. CSP 헤더는 브라우저가 특정 유형의 콘텐츠를 만났을 때 어떻게 작동해야 하는지를 알려주는 것입니다. 따라서 헤더가 특정 도메인에서만 이미지를 허용하도록 지정하면 브라우저는 해당 도메인에서만 이미지를 허용합니다.

nano 또는 선호하는 텍스트 편집기를 사용하여 settings.py를 엽니다:

  1. nano testsite/testsite/settings.py

파일의 어디에서든 다음 변수를 정의하십시오:

testsite/testsite/settings.py
# 콘텐츠 보안 정책

CSP_IMG_SRC = ("'self'")

CSP_STYLE_SRC = ("'self'")

CSP_SCRIPT_SRC = ("'self'")

이러한 규칙은 CSP의 보일러플레이트입니다. 이러한 라인들은 각각 이미지, 스타일시트 및 스크립트에 대해 허용된 소스를 나타냅니다. 현재 이들은 모두 문자열 'self'을 포함하고 있으며, 이는 자신의 도메인에서만 리소스가 허용된다는 것을 의미합니다.

작업이 완료되면 파일을 저장하고 닫으십시오.

다음 명령을 사용하여 Django 프로젝트를 실행하십시오:

  1. python manage.py runserver your-server-ip:8000

your-server-ip:8000을(를) 방문하면 사이트가 손상되었음을 확인할 수 있습니다:

예상대로 이미지가 나타나지 않고 텍스트가 기본 스타일(굵은 검정색)로 나타납니다. 이는 CSP 헤더가 시행되고 페이지가 이제 더 안전해졌음을 의미합니다. 이전에 생성한 뷰가 자신의 도메인이 아닌 도메인에서 스타일시트 및 이미지를 참조하기 때문에 브라우저가 이를 차단합니다.

이제 프로젝트에 자신의 도메인이 아닌 리소스를 차단하도록 브라우저에 지시하는 작동하는 CSP가 있습니다. 다음으로, 홈페이지의 누락된 이미지와 스타일링을 수정하기 위해 CSP를 특정 리소스를 허용하도록 수정할 것입니다.

단계 4 — 외부 리소스를 허용하도록 CSP 수정하기

이제 기본 CSP가 있으므로 사이트에서 사용 중인 것을 기반으로 수정하게 될 것입니다. 예를 들어 Adobe Fonts와 임베디드 YouTube 비디오를 사용하는 웹사이트는 이러한 리소스를 허용해야 합니다. 그러나 웹사이트가 자체 도메인 내의 이미지만 표시하는 경우 이미지 설정을 제한적인 기본값으로 유지할 수 있습니다.

첫 번째 단계는 승인해야 하는 모든 리소스를 찾는 것입니다. 이를 위해 브라우저의 개발자 도구를 사용할 수 있습니다. 요소 검사에서 네트워크 모니터를 열고 페이지를 새로 고침한 다음 차단된 리소스를 살펴보십시오:

네트워크 로그에는 CSP에 의해 두 개의 리소스가 차단되었음을 보여줍니다: fonts.googleapis.com의 스타일시트와 html.sammy-codes.com의 이미지입니다. CSP 헤더에서 이러한 리소스를 허용하려면 settings.py의 변수를 수정해야 합니다.

외부 도메인에서 리소스를 허용하려면 파일 유형과 일치하는 CSP의 일부에 도메인을 추가하십시오. 따라서 html.sammy-codes.com에서 이미지를 허용하려면 CSP_STYLE_SRChtml.sammy-codes.com을 추가하면 됩니다.

settings.py를 열고 다음을 CSP_STYLE_SRC 변수에 추가하십시오:

testsite/testsite/settings.py
CSP_IMG_SRC = ("'self'", 'https://html.sammy-codes.com')

이제 도메인에서 이미지를 허용하는 것뿐만 아니라 사이트는 이제 html.sammy-codes.com에서 이미지도 허용합니다.

인덱스 뷰는 Google Fonts를 사용합니다. Google은 여러분의 사이트에 글꼴을 제공하며(https://fonts.gstatic.com에서) 이를 적용할 스타일 시트도 제공합니다(https://fonts.googleapis.com에서). 글꼴이 로드되도록 하려면 CSP에 다음을 추가하십시오:

testsite/testsite/settings.py
CSP_STYLE_SRC = ("'self'", 'https://fonts.googleapis.com')

CSP_FONT_SRC = ("'self'", 'https://fonts.gstatic.com/')

html.sammy-codes.com에서 이미지를 허용하는 것과 유사하게, fonts.googleapis.com에서 스타일 시트 및 fonts.gstatic.com에서 글꼴을 허용합니다. fonts.googleapis.com에서 로드된 스타일 시트는 글꼴을 적용하는 데 사용됩니다. 글꼴 자체는 fonts.gstatic.com에서 로드됩니다.

파일을 저장하고 닫으십시오.

경고: self와 유사하게, CSP에서 unsafe-inline, unsafe-eval, 또는 unsafe-hashes와 같은 다른 키워드가 있습니다. 이러한 규칙을 CSP에서 사용하지 않는 것이 매우 권장됩니다. 이러한 규칙은 구현을 쉽게 만들 수 있지만 CSP를 우회하고 무용하게 만들 수 있습니다.

더 많은 정보는 “위험한 인라인 스크립트”에 대한 Mozilla 제품 문서를 참조하십시오.

이제 Google Fonts에서 스타일 및 글꼴을 사이트에 로드할 수 있으며, html.sammy-codes.com에서 이미지를 로드할 수 있습니다. 그러나 서버의 페이지를 방문하면 지금은 이미지만 로드되는 것을 알 수 있습니다. 그 이유는 HTML에서 사용된 인라인 스타일이 허용되지 않았기 때문입니다. 다음 단계에서 이를 수정하겠습니다.

단계 5 — 인라인 스크립트 및 스타일 사용하기

이 시점에서 CSP를 수정하여 외부 리소스를 허용했습니다. 그러나 뷰 내의 스타일 및 스크립트와 같은 인라인 리소스는 아직 허용되지 않았습니다. 이번 단계에서는 글꼴 스타일링을 적용할 수 있도록 이를 작동시킵니다.

인라인 스크립트와 스타일을 허용하는 두 가지 방법이 있습니다: 논스(nonces) 및 해시(hash). 인라인 스크립트와 스타일을 자주 수정하는 경우 CSP를 자주 변경하지 않도록 논스를 사용하십시오. 인라인 스크립트와 스타일을 드물게 업데이트하는 경우 해시를 사용하는 것이 합리적인 접근 방법입니다.

인라인 스크립트 허용을 위한 nonce 사용

먼저, 논스(nonce) 접근 방법을 사용하겠습니다. 논스는 각 요청마다 고유한 무작위 생성 토큰입니다. 사이트를 방문하는 두 사람이 있다면, 각각 승인된 인라인 스크립트와 스타일에 포함된 고유한 nonce를 받게 됩니다. 논스를 일회용 비밀번호로 생각하면 됩니다. 이 비밀번호는 사이트의 특정 부분이 단일 세션에 대해 실행되도록 승인합니다.

프로젝트에 논스 지원을 추가하려면 settings.py에서 CSP를 업데이트하면 됩니다. 편집할 파일을 엽니다:

  1. nano testsite/testsite/settings.py

settings.py 파일의 CSP_INCLUDE_NONCE_IN'script-src'를 추가합니다.

파일 어디에서나 CSP_INCLUDE_NONCE_IN을 정의하고 'script-src'를 추가합니다:

testsite/testsite/settings.py
# 콘텐츠 보안 정책

CSP_INCLUDE_NONCE_IN = ['script-src']

CSP_INCLUDE_NONCE_INnonce 속성을 추가할 수 있는 인라인 스크립트를 나타냅니다. CSP_INCLUDE_NONCE_IN은 여러 데이터 소스가 nonce를 지원하기 때문에 배열로 처리됩니다 (예: style-src).

파일을 저장하고 닫습니다.

인라인 스크립트에 nonce 속성을 추가하면 nonce가 생성될 수 있습니다. 이를 시도하기 위해 간단한 JavaScript 스니펫을 사용하겠습니다.

index.html을 편집합니다:

  1. nano testsite/testsite/templates/index.html

다음 스니펫을 HTML의 <head>에 추가합니다:

testsite/testsite/templates/index.html
<script>
    console.log("Hello from the console!");
</script>

이 스니펫은 브라우저 콘솔에 Hello from the console!"을 출력합니다. 그러나 프로젝트에는 인라인 스크립트에 nonce가 있어야만 허용되는 CSP가 있기 때문에 이 스크립트는 실행되지 않고 오류가 발생합니다.

페이지를 새로 고침하면 브라우저 콘솔에서 이 오류를 확인할 수 있습니다:

이전 단계에서 외부 리소스를 허용했기 때문에 이미지가 로드됩니다. 예상대로 스타일은 아직 기본값입니다. 또한 예상대로 콘솔 메시지가 출력되지 않고 오류가 반환됩니다. 이를 승인하려면 nonce를 제공해야 합니다.

이를 속성으로 추가하여 가능합니다. nonce="{{request.csp_nonce}}"이 스크립트에

testsite/testsite/templates/index.html
<script nonce="{{request.csp_nonce}}">
    console.log("Hello from the console!");
</script>

이 부분을 하이라이트된 부분과 같이 표시된 대로 편집하려면 index.html을 엽니다:

작업이 완료되면 파일을 저장하고 닫으십시오.

페이지를 새로 고치면 이제 스크립트가 실행됩니다:Inspect Element에서 확인하면 속성에 대한 값이 없음을 알 수 있습니다:

값은 보안상의 이유로 나타나지 않습니다. 브라우저가 이미 값을 처리했습니다. DOM에 액세스 할 수있는 스크립트가 값을 액세스하여 다른 스크립트에 적용하지 못하도록 숨겨져 있습니다. 대신 페이지 소스 보기를 선택하면 브라우저가 받은 내용이 표시됩니다:

페이지를 새로 고칠 때마다 nonce 값이 변경됩니다. 이는 프로젝트의 CSP 미들웨어가 각 요청에 대해 새 nonce을 생성하기 때문입니다.

nonce 값은 브라우저가 응답을 수신할 때 CSP 헤더에 추가됩니다:

브라우저가 사이트로 요청하는 모든 요청에 대해 해당 스크립트에 고유한 nonce 값이 있습니다. nonce이 CSP 헤더에서 제공되므로 Django 서버가 해당 스크립트를 실행하도록 승인했습니다:

여러 리소스에 적용 할 수있는 nonce와 함께 프로젝트를 업데이트했습니다. 예를 들어, CSP_INCLUDE_NONCE_IN을 업데이트하여 style-src를 허용할 수 있습니다. 그러나 인라인 리소스를 승인하는 더 간단한 방법이 있으며, 다음에 수행 할 작업입니다.

해시 사용하여 인라인 스타일 허용하기

인라인 스크립트 및 스타일을 허용하는 또 다른 방법은 해시를 사용하는 것입니다. 해시는 특정 인라인 리소스에 대한 고유한 식별자입니다.

예를 들어, 이것은 우리 템플릿의 인라인 스타일입니다:

testsite/testsite/templates/index.html
<style>
    h1 {
        font-family: "Yellowtail", cursive;
        margin: 0.5em 0 0 0;
        color: #0069ff;
        font-size: 4em;
        line-height: 0.6;
    }

    img {
        border-radius: 100%;
        border: 6px solid #0069ff;
    }

    .center {
        text-align: center;
        position: absolute;
        top: 50vh;
        left: 50vw;
        transform: translate(-50%, -50%);
    }
</style>

하지만 현재 스타일이 작동하지 않습니다. 브라우저에서 사이트를 보면 이미지가 성공적으로 로드되지만 글꼴과 스타일이 적용되지 않습니다:

브라우저의 콘솔에서 인라인 스타일이 CSP를 위반한다는 오류를 찾을 수 있습니다. (다른 오류가 있을 수 있지만 인라인 스타일에 대한 오류를 찾으십시오.)

이 오류는 스타일이 CSP에 의해 승인되지 않았기 때문에 발생합니다. 그러나 오류가 스타일 스니펫을 승인하는 데 필요한 해시를 제공합니다. 이 해시는 이 특정한 스타일 스니펫에 대해 고유합니다. 다른 스니펫은 결코 동일한 해시를 갖지 않습니다. 이 해시가 CSP 내에 배치되면 이 특정 스타일이로드 될 때마다 승인됩니다. 그러나 이 스타일을 수정하는 경우 새 해시를 가져와서 이전 해시를 새로운 것으로 교체해야 합니다.

이제 settings.py에서 CSP_STYLE_SRC에 해시를 추가하여 적용합니다:

  1. nano testsite/testsite/settings.py
testsite/testsite/settings.py
CSP_STYLE_SRC = ("'self' 'sha256-r5bInLZB0y6ZxHFpmz7cjyYrndjwCeDLDu/1KeMikHA='", 'https://fonts.googleapis.com')

CSP_STYLE_SRC 목록에 sha256-... 해시를 추가하면 브라우저가 스타일 시트를 오류없이로드할 수 있습니다.

파일을 저장하고 닫으십시오.

이제 브라우저에서 사이트를 다시로드하면 글꼴과 스타일이 성공적으로 로드됩니다:

인라인 스타일 및 스크립트가 이제 올바르게 작동합니다. 이 단계에서는 인라인 스타일 및 스크립트를 허용하기 위해 두 가지 다른 접근 방식, nonce 및 해시를 사용했습니다.

그러나 중요한 문제가 하나 있습니다. 큰 웹 사이트의 경우 특히 CSP를 유지하는 것은 까다로울 수 있습니다. CSP가 리소스를 차단할 때 언제 추적하여 해당 리소스가 악성 리소스인지 아니면 사이트의 고장난 부분인지를 판단할 수 있는 방법이 필요할 수 있습니다. 다음 단계에서는 Sentry를 사용하여 CSP가 생성한 모든 위반 사항을 로그에 기록하고 추적합니다.

단계 6 — Sentry를 사용하여 위반 사항 보고 (선택 사항)

CSP가 일반적으로 엄격하게 적용되므로 콘텐츠가 차단되는 경우를 파악하는 것이 좋습니다. 특히 콘텐츠가 차단되면 사이트의 일부 기능이 작동하지 않을 가능성이 높습니다. Sentry와 같은 도구를 사용하면 사용자에게 CSP가 요청을 차단할 때 알려줄 수 있습니다. 이 단계에서는 Sentry를 구성하여 CSP 위반 사항을 기록하고 보고합니다.

전제 조건으로 Sentry와 계정을 가입했습니다. 이제 프로젝트를 만듭니다.

Sentry 대시 보드의 왼쪽 상단에있는 프로젝트 탭을 클릭하십시오:

오른쪽 상단에있는 프로젝트 생성 버튼을 클릭하십시오:

여러 로고와 함께 제목이 “플랫폼 선택“인 것을 볼 수 있습니다. Django를 선택하십시오:

그런 다음, 아래쪽에 프로젝트 이름을 지정하십시오 (이 예제에서는 sammys-tutorial을 사용합니다) 그리고 프로젝트 생성 버튼을 클릭하십시오:

Sentry는 settings.py 파일에 추가할 코드 스니펫을 제공할 것입니다. 이 스니펫을 나중 단계에서 추가하려면 저장하십시오.

터미널에서 Sentry SDK를 설치하십시오:

  1. pip install --upgrade sentry-sdk

settings.py를 다음과 같이 열어보십시오:

  1. nano testsite/testsite/settings.py

파일 끝에 다음을 추가하고, 반드시 대시보드에서 가져온 값으로 SENTRY_DSN을 바꾸십시오:

testsite/testsite/settings.py
import sentry_sdk
from sentry_sdk.integrations.django import DjangoIntegration

sentry_sdk.init(
    dsn="SENTRY_DSN",
    integrations=[DjangoIntegration()],

    # traces_sample_rate를 1.0으로 설정하여 100%를 캡처
    # 성능 모니터링을 위한 트랜잭션.
    # 우리는 프로덕션에서 이 값을 조정하는 것을 추천합니다.
    traces_sample_rate=1.0,

    # 사용자를 오류에 연결하려면 (django.contrib.auth를 사용한다고 가정하면)
    # PII 데이터를 전송하는 것을 활성화할 수 있습니다.
    send_default_pii=True
)

이 코드는 Sentry에서 제공되며 애플리케이션에서 발생하는 모든 오류를 기록할 수 있도록 합니다. 이는 Sentry의 기본 구성이며 서버에서 이슈를 기록하기 위해 Sentry를 초기화할 필요가 있습니다. 기술적으로는 CSP 위반으로 인해 서버에서 Sentry를 초기화할 필요는 없지만, 비논스나 해시를 렌더링하는 데 문제가 있는 경우에는 이러한 오류가 Sentry에 기록됩니다.

파일을 저장하고 닫습니다.

프로젝트 대시보드로 돌아가서 기어 아이콘을 클릭하여 설정으로 이동하십시오:

보안 헤더 탭으로 이동하십시오:

report-uri를 복사하십시오:

다음과 같이 CSP에 추가하십시오:

testsite/testsite/settings.py
# 콘텐츠 보안 정책

CSP_REPORT_URI = "your-report-uri"

대시보드에서 복사한 값을 your-report-uri로 대체하십시오.

파일을 저장하고 닫으십시오. 이제 CSP 시행으로 인해 위반이 발생하면 Sentry가 이를 해당 URI에 기록합니다. 이를 확인하려면 CSP에서 도메인 또는 해시를 제거하거나 이전에 추가한 스크립트에서 nonce를 제거하여 시도해보십시오. 브라우저에서 페이지를 로드하면 Sentry의 Issues 페이지에 오류가 표시됩니다:

로그 수가 많아 압도된다면 settings.py에서 CSP_REPORT_PERCENTAGE를 정의하여 로그의 일부만 Sentry로 전송할 수도 있습니다.

testsite/testsite/settings.py
# 콘텐츠 보안 정책
# 로그의 10%를 Sentry로 보내기
CSP_REPORT_PERCENTAGE = 0.1

이제 CSP 위반 시 알림을 받고 Sentry에서 오류를 확인할 수 있습니다.

결론

이 기사에서는 콘텐츠 보안 정책으로 Django 애플리케이션을 보호했습니다. 외부 리소스를 허용하도록 정책을 업데이트하고, 인라인 스크립트와 스타일을 허용하기 위해 nonce 및 해시를 사용했습니다. 또한 위반 사항을 Sentry로 전송하도록 구성했습니다. 다음 단계로는 CSP를 강제하는 방법에 대해 자세히 알아보기 위해 Django CSP 문서를 확인하세요.

Source:
https://www.digitalocean.com/community/tutorials/how-to-secure-your-django-application-with-a-content-security-policy