.env にまとめるところまでは多くのプロジェクトでやっていますが、
その先の管理まで決めているケースは多くありません。
まず守るべき最低限
1. リポジトリに入れない
.env
.env.*
!.env.example
.env.example はキーの一覧だけを置き、値は空にします。
新しく参加した人が必要な項目を把握できます。
DJANGO_SECRET_KEY=
EMAIL_HOST_PASSWORD=
STRIPE_SECRET_KEY=
2. ファイル権限を絞る
chmod 600 /srv/app/.env
chown www-data:www-data /srv/app/.env
3. Webから見えないようにする
location ~ /\. { deny all; }
公開直後から /.env を狙うアクセスは確実に来ます。実際にログで確認できます。
ログに出さない
意外と多い漏洩経路がこれです。
logger.info("API request: %s", payload) # トークンが含まれていたら記録される
エラー通知にも注意が必要です。例外の詳細に接続文字列が含まれることがあります。
SENSITIVE_KEYS = {"password", "token", "secret", "authorization", "api_key"}
def mask(d: dict) -> dict:
return {k: ("***" if k.lower() in SENSITIVE_KEYS else v) for k, v in d.items()}
Django には sensitive_variables デコレータもあり、
エラーページに変数値が出るのを防げます。
環境ごとに分ける
開発と本番で同じキーを使うと、開発中の事故が本番に及びます。
開発 : テスト用のキー(決済ならテストモード)
本番 : 本番キー(アクセスできる人を限定)
開発者が本番の秘密情報を持たない状態にできるのが理想です。
定期的に入れ替える
漏洩が判明していなくても、定期的な入れ替えには意味があります。
- 退職・契約終了のタイミング
- 年1回など、期間を決めて
入れ替えを想定した設計にしておくと実行しやすくなります。
# 新旧2つを一時的に受け付ける期間を作る
VALID_KEYS = {env("API_KEY"), env("API_KEY_OLD", default="")} - {""}
漏洩したときの手順
1. まず無効化する
調査より先に、漏れた鍵を無効化します。
「まだ悪用されていないか確認してから」では遅れます。
2. 影響範囲を確認する
その鍵で何ができたのかを洗い出します。
決済キーなら取引履歴、DBパスワードならアクセスログを確認します。
3. 履歴からも消す
Git にコミットしてしまった場合、削除コミットを積むだけでは残ります。
履歴の書き換えが必要ですが、それでも
一度公開された鍵は漏れたものとして扱うのが原則です。
無効化のほうが確実で速い、という判断になります。
小規模でもできる一歩
専用の管理基盤を導入しなくても、次で十分に改善されます。
.envの権限を 600 に- ログのマスキング
- 開発と本番でキーを分ける
.env.exampleで必要な項目を明示- 入れ替え手順を文書化しておく
まとめ
- リポジトリに入れない、権限を絞る、Webから見せない
- ログとエラー通知でのマスキングを実装する
- 開発と本番で鍵を分ける
- 漏洩時は調査より先に無効化する
- 一度公開された鍵は、履歴を消しても漏れたものとして扱う