Django

Django本番環境で必ず設定すべきセキュリティ対策10選

Django セキュリティ Python

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実行 コンテナ侵害時の被害を軽減 推奨
作業前の注意
設定を変更する前に、必ず開発環境で動作確認してください。特にHTTPS関連の設定は、ローカル開発環境ではDEBUG=Trueにして無効化しておく必要があります。

対策 1SECRET_KEYを環境変数で管理する

DjangoのSECRET_KEYは、セッション、CSRFトークン、パスワードリセットトークンなどの暗号処理に使われる最重要設定です。これがソースコードに直書きされていると、リポジトリにアクセスできる全員がセッションを偽造できてしまいます。

settings.py
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のユーティリティで生成できます。

ローカル(自分のPC)
python -c "from django.core.management.utils import get_random_secret_key; print(get_random_secret_key())"
a3f8k2!@#x9z...(50文字のランダム文字列)

生成されたキーを環境変数やDockerの.envファイルに設定してください。

対策 2DEBUG=Falseを徹底する

DEBUG=Trueの状態では、以下の情報がエラーページに表示されます。

  • 完全なスタックトレース
  • ローカル変数の値(パスワードを含む可能性あり)
  • settings.pyの全設定値
  • URLパターンの一覧
settings.py
DEBUG = os.environ.get('DEBUG', 'False').lower() in ('true', '1', 'yes')
.envファイルに注意
.envファイルにDEBUG=trueを書いたまま本番にデプロイしないでください。デフォルトは必ずFalseにしましょう。

対策 3ALLOWED_HOSTSを明示的に設定する

ALLOWED_HOSTS = ['*']は、どんなHostヘッダーでも受け入れてしまいます。攻撃者はこれを悪用して、キャッシュポイズニングやパスワードリセットメールの改ざんを行えます。

settings.py
_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環境であることを伝え、セキュアな通信を強制します。

settings.py
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判定を正しく行う
SECURE_PROXY_SSL_HEADERとは
Nginx等のリバースプロキシでTLSを終端している場合、Djangoにはhttpでリクエストが届きます。この設定により、X-Forwarded-Protoヘッダーを見てhttpsであることを判定できます。

対策 5セキュリティヘッダーを設定する

HTTPレスポンスヘッダーで、ブラウザに追加のセキュリティ制約を指示できます。これらはDEBUGに関わらず常に有効にしておくべきです。

settings.py
# 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 の中に入れない
これらのヘッダーをif not DEBUG:ブロック内にだけ書くと、開発環境では保護が無効になります。もし開発環境が外部からアクセス可能な場合、脆弱性になります。常に有効にしておきましょう。

対策 6CSP(Content Security Policy)を設定する

CSPは、ブラウザに「どこからリソースを読み込んでよいか」を指示するヘッダーです。XSS攻撃が成功しても、外部スクリプトの読み込みを防げます。

CSPはNginxのレスポンスヘッダーとして設定するのが一般的です。

nginx.conf
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の安全性を高めます。

settings.py
# セッション設定
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攻撃の原因になります。アプリケーションの用途に応じた適切な上限を設定しましょう。

settings.py
# 適切なアップロード制限
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
過大な設定は危険
DjangoのデフォルトはDATA_UPLOAD_MAX_MEMORY_SIZE = 2.5MBです。用途に応じて引き上げるのは問題ありませんが、100MBや10000ファイルといった設定は、ディスク枯渇によるサービス停止のリスクがあります。

対策 9エラーメッセージで内部情報を漏洩させない

例外メッセージをそのままレスポンスに含めると、ファイルパス、ライブラリバージョン、データベース構造などの内部情報が漏洩します。

悪い例

views.py
# 内部情報が漏洩する
return JsonResponse({
    'error': f'処理エラー: {str(e)}'  # e の中身がそのまま返される
}, status=500)
{"error": "処理エラー: [Errno 2] No such file or directory: '/app/data/secret.csv'"}

ファイルパスや内部構造が攻撃者に見えてしまいます。

良い例

views.py
import logging
logger = logging.getLogger(__name__)

# 詳細はサーバー側ログに記録
logger.exception('ファイル処理中にエラーが発生')

# クライアントには汎用メッセージのみ返す
return JsonResponse({
    'error': '処理中にエラーが発生しました'
}, status=500)
{"error": "処理中にエラーが発生しました"}

対策 10Dockerコンテナを非rootで実行する

Dockerコンテナはデフォルトでrootユーザーとして実行されます。もしアプリケーションに脆弱性があり、コンテナ内でコマンド実行された場合、root権限で操作されてしまいます。

Dockerfile
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"]

確認方法

コンテナ内で実行ユーザーを確認します。

ローカル(自分のPC)
docker compose exec web whoami
appuser

rootではなくappuserと表示されれば成功です。

デプロイチェックコマンド

Djangoには本番環境向けの設定チェックコマンドが用意されています。デプロイ前に必ず実行しましょう。

ローカル(自分のPC)
python manage.py check --deploy
System check identified no issues (0 silenced).

警告が表示された場合は、各項目を確認して対応してください。

設定チェックリスト

本記事で解説した設定の一覧です。本番デプロイ前に確認してください。

設定項目 推奨値 設定場所
SECRET_KEY環境変数から取得settings.py
DEBUGFalsesettings.py
ALLOWED_HOSTSドメイン名を明示settings.py
SECURE_SSL_REDIRECTTruesettings.py
SECURE_HSTS_SECONDS31536000settings.py
SESSION_COOKIE_SECURETruesettings.py
CSRF_COOKIE_SECURETruesettings.py
SECURE_CONTENT_TYPE_NOSNIFFTruesettings.py
X_FRAME_OPTIONSDENYsettings.py
Content-Security-Policy自ドメインのみ許可nginx.conf
Dockerユーザー非rootDockerfile

まとめ

Djangoの本番環境では、フレームワークの標準機能を活かしつつ、追加のセキュリティ設定が必要です。最低限、以下の3つは必ず設定してください。

  1. SECRET_KEYを環境変数で管理し、ソースコードにハードコードしない
  2. DEBUG=Falseを徹底し、エラーページで内部情報を漏洩させない
  3. ALLOWED_HOSTSを明示的に設定し、ワイルドカードを使わない
  4. HTTPS関連設定(SSL_REDIRECT、HSTS、Secure Cookie)を有効にする
  5. セキュリティヘッダー(X-Frame-Options、Content-Type-Options)を常に有効にする
  6. CSPヘッダーで読み込み可能なリソースを制限する
  7. セッションCookieにHttpOnly、Secure、SameSite属性を設定する
  8. ファイルアップロードのサイズと数を適切に制限する
  9. エラーメッセージには汎用的な文言を使い、詳細はサーバーログに記録する
  10. Dockerコンテナは非rootユーザーで実行する

定期的にpython manage.py check --deployを実行して、設定漏れがないか確認することをお勧めします。