セキュリティ

秘密情報の管理 — .env から一歩進める

.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から見せない
  • ログとエラー通知でのマスキングを実装する
  • 開発と本番で鍵を分ける
  • 漏洩時は調査より先に無効化する
  • 一度公開された鍵は、履歴を消しても漏れたものとして扱う