あなたのプロダクションDjangoプロジェクトのセキュリティを強化する方法
著者は、寄付を行うためWrite for Donationsプログラムの一環としてCOVID-19救援基金を選びました。
はじめに
Djangoアプリケーションの開発は便利な経験になることができます。なぜなら、柔軟性とスケーラビリティを重視して構築されているからです。この考え方は、Djangoのセキュリティ重視の設定にも適用され、プロジェクトを本番環境に準備するのに役立ちます。しかし、プロジェクトをさらに安全にする方法はいくつかあります。
設定を分割することで、環境に基づく異なる構成を設定することができます。.envを利用して環境変数を設定したり、機密性の高い設定を隠したりすることで、プロジェクトの漏洩を防ぎます。また、デフォルトのURLや他の設定を変更することで、一般的なセキュリティの脆弱性を回避することができます。
最初はこれらの戦略を実施することは時間がかかるように感じるかもしれませんが、実用的なワークフローを開発することで、セキュリティや生産性を損なうことなく、プロジェクトのリリースを展開することができます。
このチュートリアルでは、環境ベースの設定、.env、およびDjangoの組み込みセキュリティ設定を実装・設定することで、Djangoプロジェクトに対してセキュリティ重視のワークフローを活用します。これらの機能は互いを補完し、展開において取る可能性のある異なるアプローチに対応したDjangoプロジェクトのバージョンの完成をもたらします。
前提条件
このガイドを始める前に、次のものが必要です。
- A pre-existing Django project. If you don’t already have one set up, you can use our How To Install Django and Set Up a Development Environment tutorial for setup. In that tutorial, you’ll use the testsite project from this tutorial as an example.For your Django project, you’ll also need Python 3 installed. You can install it by following Step 1 of our tutorial, How To Install Python 3 and Set Up a Programming Environment on an Ubuntu 20.04 Server.
- A Let’s Encrypt certificate. If you don’t already have one set up, you can use our How To Secure Nginx with Let’s Encrypt on Ubuntu 20.04 tutorial for setup.To use the Let’s Encrypt certificate, you’ll need Nginx installed. You can install it by following our tutorial How To Install Nginx on Ubuntu 20.04.
- This Django Development tutorial series is a great way to get familiar with Django’s file structure and its core settings.
Note
ステップ1:Djangoの設定の再構築
Djangoプロジェクトを確保する細部に入る前に、プロジェクトディレクトリに移動して仮想環境をアクティブにする必要があります。
- cd django-apps
- . env/bin/activate
この最初のステップでは、settings.pyファイルを環境固有の設定に再配置します。これは、例えば開発環境と本番環境のように、プロジェクトを異なる環境間で移動する必要がある場合に便利な手法です。この配置により、異なる環境での再設定が少なくなります。代わりに、後述のチュートリアルで説明されるように、環境変数を使用して設定を切り替えます。
あなたのプロジェクトのサブディレクトリに、settingsという新しいディレクトリを作成してください。
- mkdir testsite/testsite/settings
(前提として、このチュートリアルではテストサイトを使用していますが、ここにはプロジェクトの名前を代替することができます。)
このディレクトリは、現在のsettings.pyの設定ファイルを置き換えます。環境に基づく設定は、このフォルダに含まれる別々のファイルに記述されます。
新しい設定フォルダーにて、Pythonのファイルを3つ作成してください。
- cd testsite/testsite/settings
- touch base.py development.py production.py
development.pyファイルには通常開発中に使用する設定が含まれます。また、production.pyファイルには本番サーバーで使用する設定が含まれます。これらを分けておく必要があります。なぜなら、本番環境の設定には開発環境では機能しない設定が含まれるからです。例えば、HTTPSの強制、ヘッダーの追加、そして本番用データベースの使用などです。
base.pyの設定ファイルには、development.pyとproduction.pyが継承する設定が含まれます。これは冗長性を減らし、コードをより綺麗に保つためです。これらのPythonファイルはsettings.pyを置き換えるため、Djangoを混乱させないようにsettings.pyを削除します。
まだ設定ディレクトリ内にいる間に、次のコマンドを使用してsettings.pyをbase.pyに名前を変更してください。
- mv ../settings.py base.py
新しい環境設定ディレクトリのアウトラインを完成させました。プロジェクトはまだ新しい設定を理解できませんので、次はこれを修正します。
ステップ2:django-environを使用する
現在、Djangoは新しい設定ディレクトリやその内部ファイルを認識することができません。したがって、環境に基づく設定で作業を続ける前に、Djangoをdjango-environと連携させる必要があります。これは、環境変数を.envファイルから読み込む依存関係です。つまり、Djangoはプロジェクトのルートディレクトリ内の.envファイルを参照し、どの設定構成を使用するかを決定します。
プロジェクトのルートディレクトリに移動して、その後lsコマンドを使用してディレクトリの内容をリスト表示します。
- cd ../../
- ls
あなたのプロジェクトのルートディレクトリ内のファイルは、以下のようになるべきです。
db.sqlite3 manage.py testsite
django-environをインストールする。
Note: The response provided is a translation of the given English sentence into Japanese.
- pip install django-environ
現在、Djangoの設定を.envを使うようにする必要があります。これを行うためには、開発用のmanage.pyと、本番用のwsgi.pyの2つのファイルを編集します。
最初に、nanoまたはお好きなテキストエディタを使用して、manage.pyを編集するために開きます。
- 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を押してmanage.pyを保存して閉じ、Yを押して保存し、ENTERを押して閉じる。
次に、編集するためにwsgi.pyを開いてください。
- 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を押してください。
これらのファイルに追加したコードは2つのことを行います。まず最初に、Djangoが実行されるたびに(開発環境ではmanage.py、本番環境ではwsgi.py)、.envファイルを探すように指示しています。もしファイルが存在すれば、.envが推奨する設定ファイルを使用するようDjangoに指示します。存在しない場合は、デフォルトで開発設定を使用します。
最後に、現在のディレクトリに.envファイルを作成します。
- nano .env
次に、以下の行を追加して環境を開発モードに設定します。 (Tsugi ni, ikkou no gyou o tsuika shite kankyou o kaihatsu mōdo ni settei shimasu.)
DJANGO_SETTINGS_MODULE="testsite.settings.development"
「CTRL+X」を押してファイルを保存して閉じるために、「Y」を押して保存し、そして「ENTER」を押してください。
Note
プロジェクトに含めるために、.env.exampleファイルを作成することをおすすめします。そうすることで、必要な場所で簡単に新しい.envファイルを作成できます。
デフォルトでは、Djangoはtestsite.settings.developmentを使用しますが、DJANGO_SETTINGS_MODULEを例えばtestsite.settings.productionに変更すると、本番環境の設定が使用されるようになります。次に、development.pyとproduction.pyの設定を埋めていきます。
ステップ3-開発および製造の設定を作成します。
次に、base.pyを開き、開発用のdevelopment.pyと本番用のproduction.pyの別々のファイルに各環境で変更したい設定を追加します。production.pyでは、本番用のデータベースの認証情報を使用する必要があるため、それを用意してください。
Note
このチュートリアルでは、前提となるチュートリアルで使用したDjangoプロジェクトを例として使用します。base.pyからsettingsをdevelopment.pyに移動します。まず、development.pyを開いてください。
- 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を押してファイルを閉じます。
– CTRL+Xを押し、Yを押して保存し、ENTERを押してファイルを閉じます。
最初に、base.pyからインポートします。このファイルでは、base.pyから設定を継承します。その後、開発環境で変更したい設定を転送します。この場合、開発に特有の設定は以下の通りです:DEBUGは開発ではTrueにする必要がありますが、本番環境では不要です。また、DATABASESは本番用のデータベースではなく、開発用のローカルデータベースを使用します。ここではSQLiteデータベースを開発に使用しています。
Note
これは、DEBUGがまだ有効な状態で誤ってプロジェクトを本番環境にデプロイした場合に、秘密が明らかにされないようにするためです。
それによって、DEBUGを有効にしたままでプロジェクトを公開的にデプロイしないでください。それは常にプロジェクトのセキュリティを危険にさらすだけです。
次に、production.py に追加します。以下のコマンドでファイルを開いてください。
- nano testsite/settings/production.py
次に、次のコードを追加してください。プロダクションでは、development.pyと似たようなものになりますが、異なるデータベースの設定やDEBUGの値はFalseになります。
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を使用することができます。デフォルト値も含まれます。プロジェクトの本番バージョンのために既にデータベースを設定している場合は、提供された例ではなく、独自の設定を使用してください。
今、プロジェクトを.envのDjango settingsモジュールに基づいて異なる設定で構成しました。使用している設定の例に基づくと、プロジェクトを本番設定にすると、DEBUGはFalseになり、ALLOWED_HOSTSが定義され、すでにサーバーで構成済みの別のデータベースを使用し始めます。
ステップ4 ー Djangoのセキュリティ設定を使用する
Djangoには、プロジェクトに追加する準備のできたセキュリティ設定が含まれています。このステップでは、公開プロジェクトにとって必要不可欠なセキュリティ設定をプロジェクトに追加します。これらの設定は、プロジェクトが一般に公開されている場合に使用することを意図しています。開発環境ではこれらの設定の使用は推奨されません。したがって、このステップでは、これらの設定をproduction.pyの構成に制限します。
大部分の場合、これらの設定はセッションクッキー、CSRFクッキー、HTTPからHTTPSへのアップグレードなど、さまざまなウェブ機能においてHTTPSの使用を強制します。したがって、すでにドメインが設定されているサーバーを持っていない場合は、このセクションを今のところスキップしてください。デプロイ準備のためにサーバーを設定する必要がある場合は、この件に関してはおすすめの記事を「結論」で確認してください。
最初にproduction.pyを開きます。
- 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 is a list of strings that represent the host/domain names that your project can serve. This is a security measure to prevent an attacker from poisoning caches and DNS. Find more details about ALLOWED_HOSTS in the Django documentation.
- SECURE_SSL_REDIRECT redirects all HTTP requests to HTTPS (unless exempt). This means your project will always try to use an encrypted connection. You will need to have SSL configured on your server for this to work. Note that if you have Nginx or Apache configured to do this already, this setting will be redundant.
- SESSION_COOKIE_SECURE tells the browser that cookies can only be handled over HTTPS. This means cookies your project produces for activities, such as logins, will only work over an encrypted connection.
- CSRF_COOKIE_SECURE is the same as SESSION_COOKIE_SECURE but applies to your CSRF token. CSRF tokens protect against cross-site request forgery. Django CSRF protection does this by ensuring any forms submitted (for logins, signups, and so on) to your project were created by your project and not a third party.
- SECURE_BROWSER_XSS_FILTER sets the X-XSS-Protection: 1; mode=block header on all responses that do not already have it. This ensures third parties cannot inject scripts into your project. For example, if a user stores a script in your database using a public field, when that script is retrieved and displayed to other users it will not run.
「CTRL+X」を押してファイルを保存して閉じ、保存するために「Y」を押し、それから「ENTER」を押して閉じてください。
もしDjangoの異なるセキュリティ設定についてもっと詳しく知りたい場合は、彼らのドキュメントを参照してください。
Warning
追加の設定
以下の設定は、HTTP Strict Transport Security(HSTS)のサポート用です。つまり、常にSSLを使用する必要がある場合、全てのサイトがSSLを使用する必要があります。
- SECURE_HSTS_SECONDS is the amount of time in seconds HSTS is set for. If you set this for an hour (in seconds), every time you visit a web page on your website, it tells your browser that for the next hour HTTPS is the only way you can visit the site. If during that hour you visit an insecure part of your website, the browser will show an error and the insecure page will be inaccessible.
- SECURE_HSTS_PRELOAD only works if SECURE_HSTS_SECONDS is set. This header instructs the browser to preload your site. This means that your website will be added to a hard-coded list, which is implemented in popular browsers, like Firefox and Chrome. This requires that your website is always encrypted. It is important to be careful with this header. If at anytime you decide not to use encryption for your project, it can take weeks to be manually removed from the HSTS preload list.
- SECURE_HSTS_INCLUDE_SUBDOMAINS applies the HSTS header to all subdomains. Enabling this header means that both your_domain and unsecure.your_domain will require encryption, even if unsecure.your_domain is not related to this Django project.
Warning
これらの設定を実装する前に、DjangoのHSTSに関するドキュメンテーションを必ずお読みください。
自分自身のDjangoプロジェクトとこれらの設定がどのように連携するかを考慮する必要があります。全体的には、ここで議論されている設定は、ほとんどのDjangoプロジェクトにとって良い基盤です。次に、.envのさらなる使用法について見ていきます。
ステップ5 — Secretsのためのdjango-environの利用
このチュートリアルの最後の部分では、django-environを活用する方法を説明します。これにより、プロジェクトのSECRET_KEYや管理者のログインURLなど、特定の情報を隠すことができます。GitHubやGitLabなどのプラットフォームでコードを公開する予定がある場合、これは素晴らしいアイデアです。なぜなら、これらの秘密情報は公開されないからです。代わりに、ローカル環境やサーバーでプロジェクトを初期設定する際に、新しい.envファイルを作成し、これらの秘密変数を定義することができます。
このセクションで SECRET_KEY を隠さなければなりませんので、それに取り掛かれます。
あなたのプロジェクトのルートディレクトリにある.envファイルを開いてください。
- nano .env
次の行を追加し、”your_secret_key”を自分の秘密の文字列で置き換えることを確認してください。
DJANGO_SETTINGS_MODULE="testsite.settings.development"
SECRET_KEY="your_secret_key"
次に、CTRL+Xを押してファイルを保存して閉じる。保存するためにYを押し、そしてENTERを押すこと。
次に、base.pyを開いてください。
- nano testsite/settings/base.py
次のようにSECRET_KEY変数を更新してください。
. . .
<^>import environ
env = environ.Env()
environ.Env.read_env()<^>
SECRET_KEY = env('SECRET_KEY')
. . .
Note
その後、CTRL+Xを押してファイルを保存して閉じ、保存するためにYを押し、そしてENTERを押すことでファイルを閉じます。これにより、プロジェクトでは.envに保存されているSECRET_KEYが使用されるようになります。
最後に、ランダムな文字列を追加することで管理者URLを隠します。つまり、your_domain/adminに行く代わりに、your_domain/very_secret_url/adminにアクセスします。これにより、ボットや見知らぬ人々が管理者URLを見つけるのが難しくなり、管理者ログインのブルートフォース攻撃も困難になります。
もう一度.envを開いてください。
- nano .env
秘密の管理者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を使用してDjangoに管理者URLを隠すように指示してください。
- nano testsite/urls.py
Note
この変数にランダムな文字列を使用したい場合、Pythonには素晴らしいsecrets.pyライブラリがあります。彼らが提供する例は、安全なランダムな文字列を生成するための素晴らしい方法です。
以下のように管理者の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 for all communications (for example, subdomains, cookies, CSRF).
- XSS (cross-site scripting) attacks prevention.
- CSRF (cross-site request forgery) attacks prevention.
- Concealed project secret key.
- Concealed admin login URL, preventing brute-force attacks.
- Separate settings for development and production.
もしDjangoについてもっと知りたいと思ったなら、Django開発のチュートリアルシリーズをチェックしてみてください。
もしまだプロジェクトを本番環境に展開していない場合は、Ubuntu 20.04でDjango、Postgres、Nginx、Gunicornのセットアップ方法についてのチュートリアルをご覧ください。また、他のチュートリアルに関しては、弊社のDjangoトピックページもご覧いただけます。
そして、もちろん、詳細な情報については、Djangoの設定ドキュメントをお読みください。