【Django】django-environと.envで環境変数・秘密情報を管理する

Django

こんにちは、かつコーチです。

Djangoでアプリを作っていると、settings.pySECRET_KEYやデータベースのパスワードを直接書いてしまいがちです。
ですがこのままGitHubにpushしてしまうと、秘密情報が世界中に公開されてしまいます。

筆者は駆け出しの頃、個人開発のリポジトリを公開設定のままpushしてしまい、数時間後にGitHubから「秘密情報が検出されました」という警告メールが届いて青ざめた経験があります。
この記事では、そうした事故を防ぐための環境変数の管理方法を、django-environというライブラリを使って解説します。

なぜ環境変数で管理する必要があるのか

秘密情報をコードに書くリスク

環境変数とは、OSやサーバー側で管理する設定値のことです。
settings.pyのようなコードファイルに直接書く代わりに、コードの外側から値を渡す仕組みだとイメージしてください。

SECRET_KEYやDB接続情報をコードに直書きすると、以下のようなリスクがあります。

  • Gitリポジトリに履歴として残り、後から削除しても完全には消えない
  • 本番用と開発用で値を切り替えるたびにコードを書き換える必要がある
  • チームで開発する際、各自の環境差分がコードに紛れ込む

12 Factor Appの考え方

この問題を解決する考え方として有名なのがTwelve-Factor Appの「設定をコードから分離する」という原則です。
設定値(環境変数)とコードを分けておけば、開発環境・本番環境で同じコードを使い回しつつ、値だけを差し替えられます。

Djangoでこれを実現する定番ライブラリがdjango-environです。

django-environの導入と基本の書き方

インストールと.envファイルの作成

まずはライブラリをインストールします。

pip install django-environ

プロジェクトのルート(manage.pyと同じ階層)に.envファイルを作成します。

# .env
SECRET_KEY=django-insecure-xxxxxxxxxxxxxxxxxxxxxxxx
DEBUG=True
DATABASE_URL=postgres://user:password@localhost:5432/mydb
ALLOWED_HOSTS=localhost,127.0.0.1

settings.pyでの読み込み方

settings.pyenvironを初期化し、.envを読み込みます。

# settings.py
import os
import environ

BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))

env = environ.Env(
    DEBUG=(bool, False),
)

# .envファイルを読み込む
environ.Env.read_env(os.path.join(BASE_DIR, ".env"))

SECRET_KEY = env("SECRET_KEY")
DEBUG = env("DEBUG")
ALLOWED_HOSTS = env.list("ALLOWED_HOSTS")

DATABASES = {
    "default": env.db(),  # DATABASE_URLを自動でパースしてくれる
}

env.db()を使うと、DATABASE_URLの文字列を自動的にENGINENAMEなどに分解してくれるので、DB設定を手動で書く手間が省けます。

.gitignoreへの追加を忘れない

.envは絶対にGitの管理下に置いてはいけません。
.gitignoreに以下を追記しておきましょう。

.env

よくあるつまずきポイント・エラー対処

.envを読み込んでいるのに値が反映されない

❌Before

# .envをread_envせずにenv()だけ呼んでいる
env = environ.Env()
SECRET_KEY = env("SECRET_KEY")

このまま実行すると、次のようなエラーが出ます。

environ.errors.ImproperlyConfigured: Set the SECRET_KEY environment variable

筆者はこのエラーに遭遇したとき、「.envファイルは作ったのに、なぜ読み込まれないのか」としばらく悩みました。
原因はenviron.Env.read_env()を呼び忘れていたことでした。Env()のインスタンス化と.envファイルの読み込みは別の処理だと覚えておく必要があります。

✅After

env = environ.Env()
environ.Env.read_env(os.path.join(BASE_DIR, ".env"))  # これを忘れずに呼ぶ
SECRET_KEY = env("SECRET_KEY")

本番環境で環境変数が反映されない

RenderやHerokuのようなPaaSにデプロイする場合、.envファイル自体をサーバーに置く必要はありません。
そうしたサービスの管理画面上で環境変数を直接設定すれば、django-environ.envファイルがなくてもOS側の環境変数を読みにいってくれます。

「本番でも.envファイルをアップロードしなければ動かない」と誤解して、間違って.envをリポジトリに含めてしまうケースが初心者にはよくあるので注意しましょう。

応用・一歩先の使い方

環境ごとに.envファイルを分ける

開発・ステージング・本番で設定を切り替えたい場合、.env.development.env.productionのようにファイルを分け、起動時に読み込むファイルを切り替える運用も可能です。

env_file = os.environ.get("ENV_FILE", ".env")
environ.Env.read_env(os.path.join(BASE_DIR, env_file))

DEBUGの値は必ずFalseをデフォルトにする

environ.Env(DEBUG=(bool, False))のように、デフォルト値をFalseにしておくのがセキュリティ上のベストプラクティスです。
環境変数の設定漏れがあっても、本番でDEBUG=Trueのまま公開されてしまう事故を防げます。

まとめ

この記事のポイント

  • 秘密情報をsettings.pyに直書きすると、Git履歴に残り漏えいリスクがある
  • django-environを使えば.envファイルから設定値を読み込める
  • environ.Env.read_env()の呼び忘れが典型的なつまずきポイント
  • .envは必ず.gitignoreに追加する
  • PaaSデプロイ時は管理画面側の環境変数設定で代用できる

次に読むべき記事

環境変数の管理ができたら、実際にRenderへデプロイする手順もあわせて確認しておきましょう。
「DjangoアプリをRenderにデプロイする方法」の記事で、具体的な手順を解説しています。

タグ: Django, 中級者向け, デプロイ

タイトルとURLをコピーしました