Django本番環境で必ず設定すべき
セキュリティ対策10選
Djangoはデフォルトでも多くのセキュリティ機能を備えていますが、本番環境ではそれだけでは不十分です。
この記事では、実際のプロジェクトで適用したセキュリティ設定を、コード付きで解説します。
こんな人向けの記事です
- Djangoアプリを本番環境にデプロイしようとしている
- settings.pyのセキュリティ設定が不安
- 具体的な設定値をコピペで適用したい
なぜDjangoのセキュリティ設定が重要なのか
Djangoは「バッテリー同梱」のフレームワークで、CSRF保護やXSS対策などが標準で組み込まれています。しかし、本番環境で適切に設定しなければ、これらの保護は十分に機能しません。
たとえば、DEBUG=Trueのまま本番運用すると、エラー発生時にスタックトレース、環境変数、データベース設定がすべて画面に表示されてしまいます。攻撃者にとってこれ以上ない情報源です。
Djangoにはpython manage.py check --deployというデプロイチェックコマンドがありますが、すべての問題を検出できるわけではありません。この記事では、実際に本番環境で適用すべき設定を網羅的に解説します。
対策一覧
この記事で実施する対策の全体像です。
| 対策 | 効果 | 重要度 |
|---|---|---|
| SECRET_KEYの環境変数管理 | セッション偽造・CSRF予測を防止 | 必須 |
| DEBUG=False | 内部情報の漏洩を防止 | 必須 |
| ALLOWED_HOSTSの明示設定 | Hostヘッダー攻撃を防止 | 必須 |
| HTTPS関連設定 | 通信の盗聴・改ざんを防止 | 必須 |
| セキュリティヘッダー | クリックジャッキング等を防止 | 必須 |
| CSP設定 | XSS攻撃の影響を軽減 | 推奨 |
| セッションCookie保護 | セッションハイジャックを防止 | 必須 |
| アップロード制限 | DoS攻撃を防止 | 推奨 |
| エラーメッセージの制御 | 内部情報の漏洩を防止 | 推奨 |
| Docker非root実行 | コンテナ侵害時の被害を軽減 | 推奨 |
DEBUG=Trueにして無効化しておく必要があります。
対策 1SECRET_KEYを環境変数で管理する
DjangoのSECRET_KEYは、セッション、CSRFトークン、パスワードリセットトークンなどの暗号処理に使われる最重要設定です。これがソースコードに直書きされていると、リポジトリにアクセスできる全員がセッションを偽造できてしまいます。
import os
SECRET_KEY = os.environ.get('SECRET_KEY')
if not SECRET_KEY:
DEBUG = os.environ.get('DEBUG', 'False').lower() in ('true', '1', 'yes')
if DEBUG:
# 開発環境のみダミーキーを使用
SECRET_KEY = 'django-insecure-dummy-key-for-dev-only'
else:
raise ValueError('本番環境ではSECRET_KEYの環境変数設定が必須です')
SECRET_KEYが未設定の場合はエラーで起動を停止させます。誤って安全でないキーで運用されることを防ぎます。
SECRET_KEYの生成方法
安全なキーはDjangoのユーティリティで生成できます。
python -c "from django.core.management.utils import get_random_secret_key; print(get_random_secret_key())"
生成されたキーを環境変数やDockerの.envファイルに設定してください。
対策 2DEBUG=Falseを徹底する
DEBUG=Trueの状態では、以下の情報がエラーページに表示されます。
- 完全なスタックトレース
- ローカル変数の値(パスワードを含む可能性あり)
- settings.pyの全設定値
- URLパターンの一覧
DEBUG = os.environ.get('DEBUG', 'False').lower() in ('true', '1', 'yes')
.envファイルにDEBUG=trueを書いたまま本番にデプロイしないでください。デフォルトは必ずFalseにしましょう。
対策 3ALLOWED_HOSTSを明示的に設定する
ALLOWED_HOSTS = ['*']は、どんなHostヘッダーでも受け入れてしまいます。攻撃者はこれを悪用して、キャッシュポイズニングやパスワードリセットメールの改ざんを行えます。
_allowed = os.environ.get('ALLOWED_HOSTS', '')
if _allowed:
ALLOWED_HOSTS = [h.strip() for h in _allowed.split(',') if h.strip()]
elif DEBUG:
ALLOWED_HOSTS = ['*'] # 開発環境のみ許可
else:
raise ValueError('本番環境ではALLOWED_HOSTSの環境変数設定が必須です')
ALLOWED_HOSTS=example.com,www.example.com
対策 4HTTPS関連の設定を有効にする
HTTPSを使わない通信は、ネットワーク上の誰でも内容を傍受できます。以下の設定で、DjangoにHTTPS環境であることを伝え、セキュアな通信を強制します。
if not DEBUG:
SECURE_SSL_REDIRECT = True # HTTPをHTTPSにリダイレクト
SECURE_HSTS_SECONDS = 31536000 # HSTS有効期間(1年)
SECURE_HSTS_INCLUDE_SUBDOMAINS = True # サブドメインにも適用
SECURE_HSTS_PRELOAD = True # HSTSプリロードリストに登録
SECURE_PROXY_SSL_HEADER = ('HTTP_X_FORWARDED_PROTO', 'https')
| 設定 | 効果 |
|---|---|
SECURE_SSL_REDIRECT |
HTTPでアクセスされたら自動的にHTTPSにリダイレクト |
SECURE_HSTS_SECONDS |
ブラウザに「今後1年間はHTTPSのみ使う」と記憶させる |
SECURE_PROXY_SSL_HEADER |
Nginx等のリバースプロキシ経由でもHTTPS判定を正しく行う |
X-Forwarded-Protoヘッダーを見てhttpsであることを判定できます。
対策 5セキュリティヘッダーを設定する
HTTPレスポンスヘッダーで、ブラウザに追加のセキュリティ制約を指示できます。これらはDEBUGに関わらず常に有効にしておくべきです。
# DEBUGに関わらず常に適用
SECURE_CONTENT_TYPE_NOSNIFF = True # MIMEスニッフィング防止
X_FRAME_OPTIONS = 'DENY' # クリックジャッキング防止
| ヘッダー | 設定値 | 効果 |
|---|---|---|
X-Content-Type-Options |
nosniff | ブラウザがMIMEタイプを推測して実行するのを防止 |
X-Frame-Options |
DENY | iframeへの埋め込みを禁止(クリックジャッキング防止) |
if not DEBUG:ブロック内にだけ書くと、開発環境では保護が無効になります。もし開発環境が外部からアクセス可能な場合、脆弱性になります。常に有効にしておきましょう。
対策 6CSP(Content Security Policy)を設定する
CSPは、ブラウザに「どこからリソースを読み込んでよいか」を指示するヘッダーです。XSS攻撃が成功しても、外部スクリプトの読み込みを防げます。
CSPはNginxのレスポンスヘッダーとして設定するのが一般的です。
add_header Content-Security-Policy
"default-src 'self';
script-src 'self' 'unsafe-inline';
style-src 'self' 'unsafe-inline';
img-src 'self' data: blob:;
font-src 'self';
connect-src 'self' ws: wss:;
frame-ancestors 'none';"
always;
| ディレクティブ | 意味 |
|---|---|
default-src 'self' |
基本的に自ドメインのリソースのみ許可 |
script-src 'self' 'unsafe-inline' |
自ドメインのスクリプトとインラインスクリプトを許可 |
frame-ancestors 'none' |
iframeへの埋め込みを禁止(X-Frame-OptionsのCSP版) |
connect-src 'self' ws: wss: |
WebSocket接続を許可 |
frame-ancestors 'none'はX-Frame-Options: DENYのCSP版です。両方設定しておくことで、古いブラウザもカバーできます。
対策 7セッションCookieを保護する
セッションCookieが盗まれると、攻撃者はそのユーザーになりすませます。以下の設定でCookieの安全性を高めます。
# セッション設定
SESSION_COOKIE_HTTPONLY = True # JavaScriptからアクセス不可
SESSION_COOKIE_SAMESITE = 'Lax' # クロスサイトリクエストで送信しない
# CSRF Cookie設定
CSRF_COOKIE_SAMESITE = 'Strict'
# 本番環境のみ
if not DEBUG:
SESSION_COOKIE_SECURE = True # HTTPS接続時のみ送信
CSRF_COOKIE_SECURE = True # HTTPS接続時のみ送信
| 属性 | 効果 |
|---|---|
HttpOnly |
JavaScriptのdocument.cookieでアクセスできなくなる |
Secure |
HTTPS接続時のみCookieが送信される |
SameSite=Lax |
外部サイトからのPOSTリクエストでCookieが送信されない |
対策 8ファイルアップロードを制限する
ファイルアップロード制限が大きすぎると、DoS攻撃の原因になります。アプリケーションの用途に応じた適切な上限を設定しましょう。
# 適切なアップロード制限
DATA_UPLOAD_MAX_NUMBER_FILES = 100 # 1リクエストあたりのファイル数
DATA_UPLOAD_MAX_MEMORY_SIZE = 20 * 1024 * 1024 # 20MB
FILE_UPLOAD_MAX_MEMORY_SIZE = 20 * 1024 * 1024 # 20MB
DATA_UPLOAD_MAX_MEMORY_SIZE = 2.5MBです。用途に応じて引き上げるのは問題ありませんが、100MBや10000ファイルといった設定は、ディスク枯渇によるサービス停止のリスクがあります。
対策 9エラーメッセージで内部情報を漏洩させない
例外メッセージをそのままレスポンスに含めると、ファイルパス、ライブラリバージョン、データベース構造などの内部情報が漏洩します。
悪い例
# 内部情報が漏洩する
return JsonResponse({
'error': f'処理エラー: {str(e)}' # e の中身がそのまま返される
}, status=500)
ファイルパスや内部構造が攻撃者に見えてしまいます。
良い例
import logging
logger = logging.getLogger(__name__)
# 詳細はサーバー側ログに記録
logger.exception('ファイル処理中にエラーが発生')
# クライアントには汎用メッセージのみ返す
return JsonResponse({
'error': '処理中にエラーが発生しました'
}, status=500)
対策 10Dockerコンテナを非rootで実行する
Dockerコンテナはデフォルトでrootユーザーとして実行されます。もしアプリケーションに脆弱性があり、コンテナ内でコマンド実行された場合、root権限で操作されてしまいます。
FROM python:3.13-slim
WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY . .
# 非rootユーザーを作成し、必要なディレクトリの権限を設定
RUN adduser --disabled-password --no-create-home appuser \
&& mkdir -p /app/log /app/media \
&& chown -R appuser:appuser /app
# 以降はappuserとして実行
USER appuser
EXPOSE 8000
CMD ["daphne", "-b", "0.0.0.0", "-p", "8000", "config.asgi:application"]
確認方法
コンテナ内で実行ユーザーを確認します。
docker compose exec web whoami
rootではなくappuserと表示されれば成功です。
デプロイチェックコマンド
Djangoには本番環境向けの設定チェックコマンドが用意されています。デプロイ前に必ず実行しましょう。
python manage.py check --deploy
警告が表示された場合は、各項目を確認して対応してください。
設定チェックリスト
本記事で解説した設定の一覧です。本番デプロイ前に確認してください。
| 設定項目 | 推奨値 | 設定場所 |
|---|---|---|
SECRET_KEY | 環境変数から取得 | settings.py |
DEBUG | False | settings.py |
ALLOWED_HOSTS | ドメイン名を明示 | settings.py |
SECURE_SSL_REDIRECT | True | settings.py |
SECURE_HSTS_SECONDS | 31536000 | settings.py |
SESSION_COOKIE_SECURE | True | settings.py |
CSRF_COOKIE_SECURE | True | settings.py |
SECURE_CONTENT_TYPE_NOSNIFF | True | settings.py |
X_FRAME_OPTIONS | DENY | settings.py |
| Content-Security-Policy | 自ドメインのみ許可 | nginx.conf |
| Dockerユーザー | 非root | Dockerfile |
まとめ
Djangoの本番環境では、フレームワークの標準機能を活かしつつ、追加のセキュリティ設定が必要です。最低限、以下の3つは必ず設定してください。
SECRET_KEYを環境変数で管理し、ソースコードにハードコードしないDEBUG=Falseを徹底し、エラーページで内部情報を漏洩させないALLOWED_HOSTSを明示的に設定し、ワイルドカードを使わない- HTTPS関連設定(SSL_REDIRECT、HSTS、Secure Cookie)を有効にする
- セキュリティヘッダー(X-Frame-Options、Content-Type-Options)を常に有効にする
- CSPヘッダーで読み込み可能なリソースを制限する
- セッションCookieにHttpOnly、Secure、SameSite属性を設定する
- ファイルアップロードのサイズと数を適切に制限する
- エラーメッセージには汎用的な文言を使い、詳細はサーバーログに記録する
- Dockerコンテナは非rootユーザーで実行する
定期的にpython manage.py check --deployを実行して、設定漏れがないか確認することをお勧めします。