綴Photo の勉強ノートに戻る
コードレビュー学習2026.04.09

Django の SECRET_KEY を環境変数で必須化する — base のデフォルトと本番の二層構造

コードレビューセキュリティDjango

コードレビューで気づいた「設定の甘さ」と、その対策の設計を、勉強のためにまとめたものです。


この記事でわかること

  • Django の SECRET_KEY の役割と、弱いデフォルト値が招くリスク
  • 「base に開発用デフォルト / 本番・開発設定で環境変数を必須化して上書き」という二層構造の考え方
  • 重要設定を「未設定なら起動を止める」設計にする理由


概念: SECRET_KEY はアプリ全体の印鑑

Django の SECRET_KEY は アプリ全体の印鑑のようなものです。「この cookie は本物」「このセッションは改ざんされていない」「このパスワードリセットリンクは正規」といった署名処理のすべてに使われる、最重要の設定値です。

弱い値・公開された値での起動は、その印鑑を 悪意のある人にも複製できる 状態を意味します。署名できる = 偽造もできる、ということです。


共通設定 base にある「開発用デフォルト」

綴Photo の Django 設定は、共通の base.py を土台にして、環境ごとの設定(本番・開発)がそれを継承する構成になっています。その base.py に、環境変数が無いときのデフォルト値があります。

python
# base.py — 全環境が継承する共通の土台
SECRET_KEY = os.environ.get("DJANGO_SECRET_KEY", "django-insecure-dev-only-key")

このデフォルト値 django-insecure-dev-only-key は、名前からして「本番では使うな」と言っています。django-insecure- は、Django がプロジェクト生成時に自動で付ける警告プレフィックスで、「この鍵はちゃんと管理されていない=本番では差し替えろ」というサインです。

印鑑の置き場所はファイルと環境変数。DB テーブルには無い。Git のファイル base.pydjango-insecure-dev-only-keyソースに書いた文字列リポジトリを読めば見える閉じてもファイルに残るサーバのプロセス環境DJANGO_SECRET_KEYコンテナ / ホストの環境変数Git にも DB にも保存しないプロセスが止まれば消える起動後の印鑑はプロセスメモリの Django 設定。署名に使うだけで、表には書かない。左を本番で使うと、公開コードと同じ印鑑になる。

コードを読めば誰でも同じ鍵を作れて、署名を偽造できます。


対策: 本番・開発設定で「必須化」して上書きする

ここが対策です。base を継承する本番・開発の設定で、SECRET_KEY を 環境変数の必須項目 として上書きします。

python
# production.py — 本番。base を継承して上書きする
from .base import *
 
SECRET_KEY = os.environ["DJANGO_SECRET_KEY"]  # 必須。未設定なら起動時に KeyError で落ちる

ポイントは os.environ.get(...)(無ければデフォルト)ではなく os.environ[...](無ければエラー)にしていることです。

これが「base に開発用デフォルトを残しつつ、上の層で必ず必須化して上書きする」二層構造です。

上の層が下の層を上書きする。本番の鍵はファイルに無く、DB にも無い。production.pyGit のファイルwsgi / asgi / Dockerfile無ければ起動しないdevelopment.pyGit のファイルローカル起動無ければ起動しないプロセス環境DJANGO_SECRET_KEYGit にも DB にも保存しない起動時にメモリへ読む読む継承して上書き下の層 base.pyGit のファイル。開発用デフォルトが書いてある通常の起動では上書きされるので使われない起動後の印鑑はプロセスメモリの Django 設定。表には書かない。

なぜ base にデフォルトを残すのか(なぜそうなりやすいか)

「危ないなら base のデフォルトも消せばいいのでは」と思うかもしれません。だが、デフォルトを置きたくなるのには構造的な理由があります。

ローカルで毎回環境変数を設定するのは面倒だし、デフォルトがあれば git clone した直後でも一応動きます。チュートリアルやテンプレートも「とりあえず動く」を優先してデフォルト値を置きがちです。つまりこれは怠慢ではなく、開発体験を良くしようとする善意です。問題は、その善意が本番のセキュリティ設定をすり抜けてしまうことです。

だから「個人が気をつける」では防げません。「開発は楽・本番は厳格」は、個人の注意ではなくこの形で両立できます。


なぜ危険か(デフォルトのまま本番が動くと)

  • 攻撃者が公開コードからデフォルト値を知れば、署名を偽造できます
  • 弱い鍵で本番が動いても、警告が出ません

学びポイント

  • 「デフォルトがあると便利」は開発者目線です。攻撃者目線では「コードを読めば同じ鍵を作れます」
  • 重要設定は明示的に要求し、失敗を音にします(静かなバグではなく、起動失敗にします)
  • この「重要設定は起動時に明示エラー」は1つの型にできます。綴Photo では、許可ホストや CORS / CSRF のオリジンを _require_csv という共通ヘルパーで必須化し、未設定や *(全許可)を ImproperlyConfigured で弾いています。SECRET_KEY 以外にも横展開すると、設定漏れが本番に出る前に潰せます

おわりに

「設定し忘れても動く」は便利だが、セキュリティ的には危険な性質です。重要設定の未設定は、静かなバグではなく起動失敗にしてしまう ほうが安全です。

SECRET_KEY だけでなく、DB の接続情報・ストレージのアクセスキー・各種許可オリジンなど、漏れたり緩かったりすると大ごとになる設定は、すべて「本番では必須・未設定なら起動失敗」で揃えておきたい。「開発は楽に、本番は厳しく」を、設定の二層構造で両立させるのが落とし所だと感じた。


この記事は2026年4月時点の自分の理解で書いています。Django の設定パターンは安定した領域ですが、公式ドキュメントの推奨方法も合わせて参照してください。