[AI] AIエージェント時代の.envシークレット管理

[AI] AIエージェント時代の.envシークレット管理

AIエージェントはプロジェクトの全ファイルを触りうる。.env にはAPIキー・DBパスワードなど秘密情報が集まりがちで、意図せずAIのコンテキストに乗るリスクがある。リスクはそれだけではない。Web検索させた結果に悪意あるプロンプトが仕込まれていれば、AIが外部へ情報を流出させることも理論上ありうる。AIベンダーのサーバー自体がハッキングされればすべてが漏れる。歴史の浅いAI業界のサーバーにどこまで信頼を置いてよいのか、という問いでもある。これはClaude Codeに限らず、どのAIエージェントにも言える話だ。

自分自身は現状、 .env を作成してアプローチ1の deny 設定だけしている状態。Xでこの危険性と対処法が話題になっていたのを機に対処法を探したくて、気になった手法をひとまずここにまとめた。自分では未検証の内容が含まれるため、詳細は各リンク先を参照してください。

アプローチ1:deny 設定でReadツールをブロック(Claude Code固有)

.claude/settings.local.jsondeny に拒否パターンを追加すると、AIが Read ツールで該当ファイルを開こうとした際にブロックされる。

{
  "permissions": {
    "deny": [
      "Read(**/.env)",
      "Read(**/.env.*)",
      "Read(**/*.pem)",
      "Read(**/*.key)"
    ]
  }
}

注意: Read ツールへのブロックのみ有効。cat .env のようなBashコマンドには効かないため、あくまで補助的な対策。settings.local.json はGit管理外なのでチームメンバー各自の設定が必要。

アプローチ2:シークレットをシェル設定ファイルに移す

.env.local にシークレットを書かず、~/.zshenv に移す。AIがプロジェクトファイルをたどっても届かない場所に置くアプローチ。

# ~/.zshenv(AIには届かない場所に秘密情報を置く)
export VITE_SUPABASE_PUBLISHABLE_KEY="sb_publishable_xxxx"

ViteはシェルのENVを .env より優先して読み込むため、.env.local にキーがなくても動く。複数プロジェクトで別キーを使う場合は工夫が必要。

アプローチ3:1Password CLI + opx で実行時注入

.env には実際のキーの代わりに op:// 参照パスを書き、op run で起動時に注入する。AIが .env を読んでも参照パスしか見えない。

# .env(参照パスのみ)
VITE_SUPABASE_PUBLISHABLE_KEY=op://Personal/Supabase/publishable_key
op run --env-file=.env -- npm run dev

@suinさん作の opx を使うと --env-file の指定が不要になり、サブディレクトリからも動く。

opx npm run dev

1Passwordの有料アカウントが必要(個人プラン月$3程度)。

アプローチ4:Dopplerでシークレット管理

Doppler はシークレット管理SaaS。.env ファイル自体を使わず、クラウドで管理して実行時に注入する。

brew install dopplerhq/cli/doppler
doppler login
doppler setup  # プロジェクトルートで実行
 
doppler run -- npm run dev

チームのシークレットをウェブ画面で一元管理できる。外部サービス依存が許容できるなら選択肢に入る。無料プランあり。

アプローチ5:dotenvx で.envを暗号化する

dotenvx はコマンド1つで .env を暗号化できるツール。暗号化後のファイルはGitにコミットしても安全。外部サービス不要でオープンソース。

npx @dotenvx/dotenvx encrypt
# → .env が暗号化され、秘密鍵が .env.keys に生成される
dotenvx run -- npm run dev  # .env.keys の秘密鍵で自動復号して注入

.env.keys は必ず .gitignore に追加する。

アプローチ6:macOS Keychain + lkr

lkr(LLM Key Ring) はmacOS Keychainにシークレットを保存し、実行時に子プロセスへのみ渡すCLI。ファイルにもstdoutにも出力しない設計で、AIが読める場所にキーが存在しない。

cargo install lkr-cli          # Rust 1.85以上が必要
lkr set OPENAI_API_KEY         # プロンプト入力(シェル履歴に残らない)
lkr exec -- python script.py   # Keychainから取り出して注入

macOS専用。詳しくは@yottayoshidaさんの記事を参照。

アプローチ7:.env ごと捨てる

こちらのポストで紹介されていた、根本から .env に頼らない方針。

  • .env を消す.env.local 含め、LLMが読める場所には一切置かない
  • クラウドでデプロイ確認 — 実績あるクラウドのキー管理の方が、AIが勝手に読むより遥かに安全で管理も楽
  • ローカルで外部キーが必要な場合 — AIにシェルスクリプトを書かせて、起動時にプロンプト入力させる

まとめ

アプローチ 導入コスト 根本解決 チーム適用
deny設定 すぐできる △(Bash抜け道あり) 各自設定が必要
~/.zshenv 設定簡単 ○(プロジェクト外) 全員が設定要
1Password + opx 初期コストあり ◎(参照パスのみ残る) 1Passwordが必要
Doppler 初期コストあり ◎(ファイルレス) ウェブで一元管理
dotenvx コマンド1つ ◎(暗号化済み) .envをGitで共有可
lkr Rust環境が必要 ◎(Keychainのみ) macOS専用
.envを捨てる 運用見直しが必要 ◎(そもそも存在しない) クラウド前提
  • 今すぐ何かしたい → deny設定
  • 個人開発や小チーム → ~/.zshenv
  • 1Passwordユーザー → 1Password CLI + opx
  • 外部サービスなしでチーム共有 → dotenvx
  • チーム開発・複数環境 → Doppler
  • macOS + Rustがある → lkr
  • 運用ごと見直したい → .env を捨てる