웹사이트 검색

프로덕션 Django 프로젝트의 보안을 강화하는 방법


저자는 Write for DOnations 프로그램을 선택했습니다.

소개

Django 애플리케이션 개발은 유연하고 확장 가능하도록 구성되어 있기 때문에 편리한 경험이 될 수 있습니다. 이 전제는 프로덕션 프로젝트를 준비하는 데 도움이 되는 Django의 보안 지향 설정으로 확장됩니다. 그러나 프로젝트 보안을 강화하는 방법에는 여러 가지가 있습니다.

설정을 나누면 환경에 따라 다른 구성을 설정할 수 있습니다. 환경 변수를 설정하거나 기밀 설정을 숨기는 데 .env를 활용하면 프로젝트를 손상시킬 수 있는 세부 정보를 공개하지 않습니다. 그리고 기본 URL 및 기타 설정을 변경하면 일반적인 보안 취약성을 방지하는 데 도움이 됩니다.

이러한 전략을 구현하는 것은 처음에는 시간이 많이 걸리는 것처럼 보일 수 있지만 실용적인 워크플로를 개발하면 보안이나 생산성을 손상시키지 않고 프로젝트 릴리스를 배포할 수 있습니다.

이 자습서에서는 환경 기반 설정, .env 및 Django의 기본 제공 보안 설정을 구현하고 구성하여 Django 프로젝트에 대한 보안 지향 워크플로를 활용합니다. 이러한 기능은 모두 서로를 보완하며 배포에 사용할 수 있는 다양한 접근 방식에 대해 준비된 Django 프로젝트 버전을 생성합니다.

전제 조건

이 가이드를 시작하기 전에 다음이 필요합니다.

  • 기존 Django 프로젝트. 아직 설정하지 않은 경우 Django 설치 방법 및 개발 환경 설정 자습서를 사용하여 설정할 수 있습니다. 이 자습서에서는 이 자습서의 testsite 프로젝트를 예로 사용합니다.\n
    • Django 프로젝트의 경우 Python 3도 설치해야 합니다. Ubuntu 20.04 서버에 Python 3를 설치하고 프로그래밍 환경을 설정하는 방법 자습서의 1단계에 따라 설치할 수 있습니다.

    • Let’s Encrypt 인증서를 사용하려면 Nginx가 설치되어 있어야 합니다. Ubuntu 20.04에 Nginx를 설치하는 방법 튜토리얼을 따라 설치할 수 있습니다.

    참고: 기존 Django 프로젝트를 사용하는 경우 요구 사항이 다를 수 있습니다. 이 자습서에서는 특정 프로젝트 구조를 제안합니다. 그러나 필요에 따라 이 자습서의 각 섹션을 개별적으로 사용할 수도 있습니다.

    1단계 — Django 설정 재구성

    Django 프로젝트를 보호하는 기본 사항에 들어가기 전에 프로젝트 디렉토리로 이동하여 가상 환경을 활성화해야 합니다.

    1. cd django-apps
    2. . env/bin/activate

    이 첫 번째 단계에서는 settings.py 파일을 환경별 구성으로 재정렬하는 것으로 시작합니다. 이는 예를 들어 개발 및 프로덕션과 같은 서로 다른 환경 간에 프로젝트를 이동해야 하는 경우 좋은 방법입니다. 이 배열은 다른 환경에 대한 재구성이 적다는 것을 의미합니다. 대신 환경 변수를 사용하여 구성 간에 전환합니다. 이에 대해서는 자습서의 뒷부분에서 설명합니다.

    프로젝트의 하위 디렉터리에 settings라는 새 디렉터리를 만듭니다.

    1. mkdir testsite/testsite/settings

    (전제 조건에 따라 이 자습서에서는 testsite를 사용하지만 여기에서 프로젝트 이름을 대체할 수 있습니다.)

    이 디렉토리는 현재 settings.py 구성 파일을 대체합니다. 모든 환경 기반 설정은 이 폴더에 포함된 별도의 파일에 있습니다.

    settings 폴더에서 세 개의 Python 파일을 만듭니다.

    1. cd testsite/testsite/settings
    2. touch base.py development.py production.py

    development.py 파일에는 일반적으로 개발 중에 사용하게 될 설정이 포함됩니다. 그리고 production.py에는 프로덕션 서버에서 사용할 설정이 포함됩니다. 프로덕션 구성은 개발 환경에서 작동하지 않는 설정을 사용하므로 이러한 설정을 별도로 유지해야 합니다. 예를 들어 HTTPS를 강제로 사용하고, 헤더를 추가하고, 프로덕션 데이터베이스를 사용합니다.

    base.py 설정 파일에는 development.pyproduction.py가 상속할 설정이 포함됩니다. 이는 중복을 줄이고 코드를 더 깔끔하게 유지하는 데 도움이 됩니다. 이러한 Python 파일은 settings.py를 대체하므로 Django 혼동을 피하기 위해 이제 settings.py를 제거합니다.

    여전히 settings 디렉토리에 있는 동안 다음 명령을 사용하여 settings.py의 이름을 base.py로 바꿉니다.

    1. mv ../settings.py base.py

    새 환경 기반 설정 디렉토리의 개요를 방금 완료했습니다. 프로젝트는 아직 새 구성을 이해하지 못하므로 다음에는 이를 수정합니다.

    2단계 — django-environ 사용

    현재 Django는 새 설정 디렉토리 또는 내부 파일을 인식하지 못합니다. 따라서 환경 기반 설정으로 작업을 계속하기 전에 Django가 django-environ과 함께 작동하도록 해야 합니다. .env 파일에서 환경 변수를 로드하는 종속성입니다. 즉, Django는 프로젝트의 루트 디렉터리에 있는 .env 파일 내부를 살펴보고 사용할 설정 구성을 결정합니다.

    프로젝트의 루트 디렉터리로 이동한 다음 ls 명령을 사용하여 디렉터리의 콘텐츠를 나열합니다.

    1. cd ../../
    2. ls

    프로젝트의 루트 디렉터리에 있는 파일은 다음과 같아야 합니다.

    Output
    db.sqlite3 manage.py testsite

    django-environ 설치:

    1. pip install django-environ

    이제 .env를 사용하도록 Django를 구성해야 합니다. 이를 위해 개발용 manage.py와 프로덕션용 wsgi.py의 두 파일을 편집합니다.

    nano 또는 선호하는 텍스트 편집기를 사용하여 편집하기 위해 manage.py를 열어 시작합니다.

    1. nano manage.py

    다음 강조 표시된 코드를 추가합니다.

    import os
    import sys
    <^>import environ
    
    environ.Env.read_env()<^>
    
    def main():
        os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'testsite.settings')
    
        try:
            from django.core.management import execute_from_command_line
        except ImportError as exc:
            raise ImportError(
                "Couldn't import Django. Are you sure it's installed and "
                "available on your PYTHONPATH environment variable? Did you "
                "forget to activate a virtual environment?"
            ) from exc
        execute_from_command_line(sys.argv)
    
    
    if __name__ == '__main__':
        main()
    

    CTRL+X를 누르고 Y를 눌러 저장한 다음 ENTER를 눌러 manage.py를 저장하고 닫습니다.

    다음으로 편집을 위해 wsgi.py를 엽니다.

    1. nano testsite/wsgi.py

    다음 강조 표시된 줄을 추가합니다.

    
    import os
    <^>import environ
    
    environ.Env.read_env()<^>
    
    from django.core.wsgi import get_wsgi_application
    
    os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'testsite.settings')
    
    application = get_wsgi_application()
    

    CTRL+X를 누르고 Y를 눌러 저장한 다음 ENTER를 눌러 파일을 저장하고 닫습니다.

    이 두 파일에 추가한 코드는 두 가지 작업을 수행합니다. 첫째, Django가 실행될 때마다(개발용 manage.py, 프로덕션용 wsgi.py) .env 를 찾도록 지시하는 것입니다. 파일. 파일이 존재하면 .env가 권장하는 설정 파일을 사용하도록 Django에 지시합니다. 그렇지 않으면 기본적으로 개발 구성을 사용합니다.

    마지막으로 현재 디렉토리에 .env를 생성합니다.

    1. nano .env

    이제 다음 줄을 추가하여 환경을 개발로 설정합니다.

    DJANGO_SETTINGS_MODULE="testsite.settings.development"
    

    CTRL+X를 누르고 Y를 눌러 저장한 다음 ENTER를 눌러 파일을 저장하고 닫습니다.

    참고: 커밋에 포함되지 않도록 .gitignore 파일에 .env를 추가하세요. 이 파일을 사용하여 공개적으로 표시하고 싶지 않은 암호 및 API 키와 같은 데이터를 포함합니다. 프로젝트가 실행되는 모든 환경에는 해당 특정 환경에 대한 설정이 포함된 자체 .env가 있습니다.

    필요할 때마다 새 .env를 쉽게 만들 수 있도록 프로젝트에 포함할 .env.example을 만드는 것이 좋습니다.

    따라서 기본적으로 Django는 testsite.settings.development를 사용하지만 DJANGO_SETTINGS_MODULEtestsite.settings.production 예를 들어 프로덕션 구성을 사용하기 시작합니다. 다음으로 development.pyproduction.py 설정 구성을 채웁니다.

    3단계 - 개발 및 프로덕션 설정 만들기

    다음으로 base.py를 열고 별도의 development.pyproduction.py에서 각 환경에 대해 수정하려는 구성을 추가합니다. 파일. production.py는 프로덕션 데이터베이스 자격 증명을 사용해야 하므로 사용 가능한 자격 증명이 있는지 확인하십시오.

    참고: 환경에 따라 구성해야 하는 설정을 결정하는 것은 사용자의 책임입니다. 이 자습서에서는 프로덕션 및 개발 설정(즉, 보안 설정 및 별도의 데이터베이스 구성)에 대한 일반적인 예만 다룹니다.

    이 자습서에서는 전제 조건 자습서의 Django 프로젝트를 예제 프로젝트로 사용하고 있습니다. base.py에서 development.py로 설정을 이동합니다. development.py를 열어 시작합니다.

    1. nano testsite/settings/development.py

    그런 다음 다음 코드를 추가합니다.

    import os
    from .base import *
    
    DEBUG = True
    
    DATABASES = {
        'default': {
            'ENGINE': 'django.db.backends.sqlite3',
            'NAME': os.path.join(BASE_DIR, 'db.sqlite3'),
        }
    }
    

    CTRL+X를 누르고 Y를 눌러 저장한 다음 ENTER를 눌러 파일을 저장하고 닫습니다.

    먼저 base.py에서 가져옵니다. 이 파일은 base.py에서 설정을 상속합니다. 그런 다음 개발 환경에 대해 수정하려는 설정을 전송합니다. 이 경우 개발 관련 설정은 다음과 같습니다. 프로덕션 데이터베이스 대신 로컬 데이터베이스인 DATABASES. 여기에서 개발을 위해 SQLite 데이터베이스를 사용하고 있습니다.

    참고: 보안을 위해 Django의 DEBUG 출력에는 API, KEY, PASS, SECRET 문자열을 포함할 수 있는 설정이 표시되지 않습니다. , SIGNATURE 또는 TOKEN.

    이는 DEBUG가 활성화된 상태에서 실수로 프로덕션에 프로젝트를 배포하는 경우 비밀이 노출되지 않도록 하기 위한 것입니다.

    즉, DEBUG가 활성화된 상태에서 공개적으로 프로젝트를 배포하지 마십시오. 프로젝트의 보안을 위험에 빠뜨릴 뿐입니다.

    다음으로 production.py에 추가합니다. 다음 명령으로 파일을 엽니다.

    1. nano testsite/settings/production.py

    그런 다음 다음 코드를 추가합니다. 프로덕션은 development.py와 유사하지만 데이터베이스 구성이 다르고 DEBUGFalse로 설정됩니다.

    import os
    from .base import *
    import environ
    
    env = environ.Env()
    environ.Env.read_env()
    
    DEBUG = False
    
    ALLOWED_HOSTS = []
    
    DATABASES = {
        'default': {
            'ENGINE': env('SQL_ENGINE', default='django.db.backends.sqlite3'),
            'NAME': env('SQL_DATABASE', default=os.path.join(BASE_DIR, 'db.sqlite3')),
            'USER': env('SQL_USER', default='user'),
            'PASSWORD': env('SQL_PASSWORD', default='password'),
            'HOST': env('SQL_HOST', default='localhost'),
            'PORT': env('SQL_PORT', default=''),
        }
    }
    

    CTRL+X를 누르고 Y를 눌러 저장한 다음 ENTER를 눌러 파일을 저장하고 닫습니다.

    제공된 예제 데이터베이스 구성의 경우 .env를 사용하여 기본값이 포함된 지정된 각 자격 증명을 구성할 수 있습니다. 프로젝트의 프로덕션 버전에 대한 데이터베이스를 이미 설정했다고 가정하면 제공된 예제 대신 구성을 사용하십시오.

    이제 .envDJANGO_SETTINGS_MODULE에 따라 다른 설정을 사용하도록 프로젝트를 구성했습니다. 사용한 예제 설정에서 프로덕션 설정을 사용하도록 프로젝트를 설정하면 DEBUGFalse가 되고 ALLOWED_HOSTS가 정의되며 서버에 (이미) 구성한 다른 데이터베이스를 사용하기 시작합니다.

    4단계 — Django의 보안 설정 작업

    Django에는 프로젝트에 추가할 수 있는 보안 설정이 포함되어 있습니다. 이 단계에서는 프로덕션 프로젝트에 필수적인 것으로 간주되는 보안 설정을 프로젝트에 추가합니다. 이러한 설정은 프로젝트를 공개적으로 사용할 수 있을 때 사용하기 위한 것입니다. 개발 환경에서는 이러한 설정을 사용하지 않는 것이 좋습니다. 따라서 이 단계에서는 이러한 설정을 production.py 구성으로 제한합니다.

    대부분의 경우 이러한 설정은 세션 쿠키, CSRF 쿠키, HTTP를 HTTPS로 업그레이드 등과 같은 다양한 웹 기능에 대해 HTTPS 사용을 강제합니다. 따라서 도메인을 가리키는 서버를 아직 설정하지 않은 경우 지금은 이 섹션을 보류하십시오. 배포 준비가 된 서버를 설정해야 하는 경우 이에 대한 제안 기사에 대한 결론을 확인하십시오.

    먼저 production.py를 엽니다.

    1. nano testsite/settings/production.py

    코드 다음의 설명에 따라 파일에서 프로젝트에 대해 작동하는 강조 표시된 설정을 추가합니다.

    import os
    from .base import *
    import environ
    
    env = environ.Env()
    environ.Env.read_env()
    
    DEBUG = False
    
    ALLOWED_HOSTS = ['your_domain', 'www.your_domain']
    
    DATABASES = {
        'default': {
            'ENGINE': env('SQL_ENGINE', default='django.db.backends.sqlite3'),
            'NAME': env('SQL_DATABASE', default=os.path.join(BASE_DIR, 'db.sqlite3')),
            'USER': env('SQL_USER', default='user'),
            'PASSWORD': env('SQL_PASSWORD', default='password'),
            'HOST': env('SQL_HOST', default='localhost'),
            'PORT': env('SQL_PORT', default=''),
        }
    }
    
    SECURE_SSL_REDIRECT = True
    
    SESSION_COOKIE_SECURE = True
    
    CSRF_COOKIE_SECURE = True
    
    SECURE_BROWSER_XSS_FILTER = True
    

    • ALLOWED_HOSTS는 프로젝트가 제공할 수 있는 호스트/도메인 이름을 나타내는 문자열 목록입니다. 이것은 공격자가 캐시와 DNS를 오염시키는 것을 방지하기 위한 보안 조치입니다. Django 설명서에서 ALLOWED_HOSTS에 대한 자세한 내용을 확인하세요.
    • SECURE_SSL_REDIRECT는 모든 HTTP 요청을 HTTPS로 리디렉션합니다(예외 제외). 이는 프로젝트가 항상 암호화된 연결을 사용하려고 시도함을 의미합니다. 이것이 작동하려면 서버에 SSL을 구성해야 합니다. 이미 이 작업을 수행하도록 Nginx 또는 Apache를 구성한 경우 이 설정은 중복됩니다.
    • SESSION_COOKIE_SECURE는 쿠키가 HTTPS를 통해서만 처리될 수 있음을 브라우저에 알립니다. 즉, 로그인과 같은 활동을 위해 프로젝트에서 생성하는 쿠키는 암호화된 연결을 통해서만 작동합니다.
    • CSRF_COOKIE_SECURESESSION_COOKIE_SECURE와 동일하지만 CSRF 토큰에 적용됩니다. CSRF 토큰은 교차 사이트 요청 위조로부터 보호합니다. Django CSRF 보호는 프로젝트에 제출된 모든 양식(로그인, 가입 등)이 제3자가 아닌 프로젝트에서 생성되었는지 확인하여 이를 수행합니다.
    • SECURE_BROWSER_XSS_FILTERX-XSS-Protection: 1을 설정합니다. 아직 가지고 있지 않은 모든 응답의 mode=block 헤더. 이렇게 하면 제3자가 프로젝트에 스크립트를 삽입할 수 없습니다. 예를 들어, 사용자가 공용 필드를 사용하여 데이터베이스에 스크립트를 저장하는 경우 해당 스크립트가 검색되어 다른 사용자에게 표시될 때 실행되지 않습니다.

    CTRL+X를 누르고 Y를 눌러 저장한 다음 ENTER를 눌러 파일을 저장하고 닫습니다.

    Django 내에서 사용할 수 있는 다양한 보안 설정에 대해 자세히 알아보려면 설명서를 확인하세요.

    경고: Django 문서에는 SECURE_BROWSER_XSS_FILTER에 전적으로 의존해서는 안 된다고 명시되어 있습니다. 입력을 확인하고 삭제하는 것을 잊지 마십시오.

    추가 세팅

    다음 설정은 HSTS(HTTP Strict Transport Security)를 지원하기 위한 것입니다. 즉, 전체 사이트에서 항상 SSL을 사용해야 합니다.

    • SECURE_HSTS_SECONDS는 HSTS가 설정된 시간(초)입니다. 이를 1시간(초)으로 설정하면 웹 사이트의 웹 페이지를 방문할 때마다 다음 1시간 동안 HTTPS가 사이트를 방문할 수 있는 유일한 방법임을 브라우저에 알립니다. 해당 시간 동안 웹사이트의 안전하지 않은 부분을 방문하면 브라우저에 오류가 표시되고 안전하지 않은 페이지에 액세스할 수 없습니다.
    • SECURE_HSTS_PRELOADSECURE_HSTS_SECONDS가 설정된 경우에만 작동합니다. 이 헤더는 브라우저에 HSTS 사전 로드 목록을 지시합니다.
    • SECURE_HSTS_INCLUDE_SUBDOMAINS는 HSTS 헤더를 모든 하위 도메인에 적용합니다. 이 헤더를 활성화하면 unsecure.your_domain이 이 Django 프로젝트와 관련이 없더라도 your_domainunsecure.your_domain 모두 암호화가 필요함을 의미합니다.< /리>

    경고: 이러한 추가 설정을 잘못 구성하면 상당한 시간 동안 사이트가 중단될 수 있습니다.

    이러한 설정을 구현하기 전에 HSTS의 Django 설명서를 읽어보세요.

    이러한 설정이 자신의 Django 프로젝트에서 어떻게 작동하는지 고려해야 합니다. 전반적으로 여기에서 논의된 구성은 대부분의 Django 프로젝트를 위한 좋은 기반입니다. 다음으로 .env의 추가 사용법을 검토합니다.

    5단계 — 비밀에 django-environ 사용

    이 튜토리얼의 마지막 부분은 django-environ을 활용하는 데 도움이 될 것입니다. 이렇게 하면 프로젝트의 SECRET_KEY 또는 관리자의 로그인 URL과 같은 특정 정보를 숨길 수 있습니다. 이러한 비밀은 게시되지 않으므로 GitHub 또는 GitLab과 같은 플랫폼에 코드를 게시하려는 경우 좋은 생각입니다. 대신 로컬 환경이나 서버에서 프로젝트를 처음 설정할 때마다 새 .env 파일을 만들고 해당 비밀 변수를 정의할 수 있습니다.

    이 섹션에서 작업을 시작하려면 SECRET_KEY를 숨겨야 합니다.

    프로젝트의 루트 디렉터리에서 .env 파일을 엽니다.

    1. nano .env

    그리고 다음 줄을 추가하여 your_secret_key를 자신의 비밀 문자열로 바꾸십시오.

    DJANGO_SETTINGS_MODULE="testsite.settings.development"
    SECRET_KEY="your_secret_key"
    

    그런 다음 CTRL+X를 누르고 Y를 눌러 저장한 다음 ENTER를 눌러 파일을 저장하고 닫습니다.

    다음으로 base.py를 엽니다.

    1. nano testsite/settings/base.py

    다음과 같이 SECRET_KEY 변수를 업데이트합니다.

    . . .
    <^>import environ
    
    env = environ.Env()
    environ.Env.read_env()<^>
    
    SECRET_KEY = env('SECRET_KEY')
    . . .
    

    참고: SECRET_KEY는 실제 비밀 키로 교체하면 안 됩니다. SECRET_KEY 변수는 그대로 두고 실제 비밀 키는 .env 파일에 추가해야 합니다.

    그런 다음 CTRL+X를 누르고 Y를 눌러 저장한 다음 ENTER를 눌러 파일을 저장하고 닫습니다. 이제 프로젝트는 .env에 있는 SECRET_KEY를 사용합니다.

    마지막으로 긴 임의 문자 문자열을 추가하여 관리 URL을 숨깁니다. 따라서 your_domain/admin으로 이동하는 대신 your_domain/very_secret_url/admin으로 이동합니다. 이렇게 하면 봇과 낯선 사람 모두 귀하의 관리자 URL을 찾는 데 어려움을 겪게 되어 귀하의 관리자 로그인을 무차별 대입하는 데 더 어려움을 겪게 됩니다.

    .env를 다시 엽니다.

    1. nano .env

    그리고 SECRET_ADMIN_URL 변수를 추가합니다.

    DJANGO_SETTINGS_MODULE="testsite.settings.development"
    SECRET_KEY="your_secret_key"
    SECRET_ADMIN_URL="very_secret_url"
    

    CTRL+X를 누르고 Y를 눌러 저장한 다음 ENTER를 눌러 파일을 저장하고 닫습니다.

    이제 SECRET_ADMIN_URL을 사용하여 관리자 URL을 숨기도록 Django에 지시합니다.

    1. nano testsite/urls.py

    참고: very_secret_url을 자신의 비밀 URL로 바꾸는 것을 잊지 마십시오.

    이 변수에 임의의 문자열을 사용하려는 경우 Python은 안전한 임의 문자열을 생성하기 위한 작은 Python 프로그램을 만드는 훌륭한 방법인 환상적인 예제를 제공합니다.

    다음과 같이 관리 URL을 편집합니다.

    from django.contrib import admin
    from django.urls import path
    <^>import environ
    
    env = environ.Env()
    environ.Env.read_env()<^>
    
    urlpatterns = [
        path(env('SECRET_ADMIN_URL') + '/admin/', admin.site.urls),
    ]
    

    CTRL+X를 누르고 Y를 눌러 저장한 다음 ENTER를 눌러 파일을 저장하고 닫습니다.

    이제 /admin/ 대신 /very_secret_url/admin/에서 관리자 로그인 페이지를 찾을 수 있습니다.

    결론

    이 자습서에서는 다양한 환경에서 쉽게 사용할 수 있도록 현재 Django 프로젝트를 구성했습니다. 이제 프로젝트는 비밀 및 설정을 처리하기 위해 django-environ을 활용합니다. 이제 프로덕션 설정에 Django의 내장 보안 기능이 활성화되었습니다.

    모든 권장 보안 구성 요소를 활성화하고 지침에 따라 다시 구현한 경우 프로젝트에는 다음과 같은 주요 기능이 있습니다.

    • 모든 통신용 SSL/HTTPS(예: 하위 도메인, 쿠키, CSRF)
    • XSS(교차 사이트 스크립팅) 공격 방지.
    • CSRF(교차 사이트 요청 위조) 공격 방지.
    • 숨겨진 프로젝트 비밀 키.
    • 숨겨진 관리자 로그인 URL로 무차별 대입 공격을 방지합니다.
    • 개발 및 생산을 위한 별도의 설정.

    Django에 대해 자세히 알아보려면 Django 개발에 대한 자습서 시리즈를 확인하십시오.

    또한 아직 프로젝트를 프로덕션에 넣지 않은 경우 Django 주제 페이지에서 추가 자습서에 대한 자습서를 참조하십시오.

    물론 자세한 내용은 Django의 설정 문서를 읽어보십시오.