🔐

ClaudeCodeに.envを勝手に読ませないためのベストプラクティス

に公開

この記事の背景

先日、Codex にタスクを依頼したら、別プロジェクトで使っていた OpenAI の API キーを勝手に読み込んで Whisper を実行していました。
たまたま小さな文字起こし処理だったので実害は軽微だったけど、これが本番用の Stripe キーや DB パスワードだったらと思うとぞっとします。

先日、Qiita 記事「Claude Codeはプロンプトインジェクションで.envを漏洩させるのか?検証してみた」が話題になりました。
プロンプトインジェクション攻撃で Claude Code が .env の中身を標準出力に書き出してしまう実例が丁寧に検証されています。

この記事では、この問題をもう少し広く捉えます。プロンプトインジェクションに限らず、AIコーディングツールが .env を自動的に読み込む挙動そのものが危ないよね、という話です。2026年3月7日時点で海外の開発者コミュニティ(GitHub Issues、Reddit、技術ブログ)で実践されている防御策を整理しました。

結論から言うと、.claudeignore.cursorignore だけだと不十分で、direnv + 1Password CLI(または Doppler)でディスクから平文のシークレットを排除し、ツール別の deny / フック設定やサンドボックスを組み合わせる多層防御が、現時点で最も支持されている構成です。

問題の全体像

Cursor、Claude Code、GitHub Copilot などの AI コーディングツールには、プロジェクトディレクトリ内の .env.env.local などのファイルを自動的にロードする挙動があります。

これが引き起こす問題は3つあります。

1. 自プロジェクト内の .env 漏洩

Knostic 社が 2025年12月に公開した調査で、Claude Code が .env ファイルをユーザーの同意なくメモリに読み込む挙動が確認されています。同社 CEO の Gadi Evron 氏自身も、Claude Code 使用中に Gemini API キーがテストファイルに混入し GitHub にプッシュされたケースを報告しています(自分の会社の CEO がやらかすの、リアルすぎる)。

参考:Knostic - From .env to Leakage: Mishandling of Secrets by Coding Agents

2. 他プロジェクトの .env 混入

Cursor や Copilot がワークスペース全体をインデックスする過程で、隣接するプロジェクトの .env がコンテキストに混入するケース。Reddit では、他プロジェクトの Stripe キーが誤って使用されて本番請求が発生した報告や、OpenAI の課金が急増した報告が複数上がっています。僕の Codex の件もこれに近い。

3. .ignore / deny 設定の不完全性

2026年1月、The Register が「Claude Code は .claudeignore に明記しても .env を読み込む」と報じました。GitHub Issues でも、settings.jsondeny パーミッション自体が機能しないバグが報告されています(Issue #6699)。

参考:The Register - Claude Code ignores ignore rules meant to block secrets (2026/01)

.ignoredeny だけでは足りない理由

.claudeignore.cursorignoresettings.jsondeny ルールは、いずれも「AI にファイルを読ませない」ための設定です。第一防衛線としては有用だけど、これだけで安心するのは危ない。理由は2つ。

理由1:設定自体が無視されるケースがある

前述の deny ルールが効かないバグ。2026年3月時点では修正されているけど、セキュリティ設定でバグがあった事実は気になります。

理由2:AI はファイルを「読む」だけじゃなく「実行」もする

Michael Hannecke 氏が 2026年2月の記事で指摘している点です。たとえば op run -- npm test で 1Password からシークレットを注入した場合、Unix のプロセス継承で環境変数がテストランナーの子プロセスに引き継がれる。Claude Code がデバッグ目的で printenv を実行すれば、注入済みのシークレットがターミナルに表示されてしまう。

deny ルールはファイルの読み取りをブロックするものなので、実行時に展開された環境変数は守れない。Hannecke 氏はこれを「secrets at rest(静止時の保護)と secrets at runtime(実行時の保護)は別の脅威モデル」と整理しています。

この2つの理由から、2026年現在は複数レイヤーの組み合わせが主流です。確実性が高い順に紹介します。

参考:Michael Hannecke - Your Vault Protects Your Secrets — Until Claude Code Runs Your Tests

Tier 1:ディスクから平文のシークレットを排除する

海外コミュニティで最も支持されている根本対策です。

.env ファイルに本物の API キーを書かず、シークレットマネージャーへの参照文字列op://Private/OpenAI/api-key など)だけを置く。実行時に初めて本物の値が注入される仕組みにすることで、AI がファイルを読んでも実際のキーにはアクセスできなくなります。

direnv + 1Password CLI の構成

多くの開発者が採用しているのがこの組み合わせ。

direnv はディレクトリ移動時に .envrc を自動で読み込んで、そのディレクトリ内でのみ環境変数を有効にするツール。プロジェクトごとにスコープが分離されるので、他プロジェクトのキー混入を物理的に防げます。

1Password CLIopコマンド)は、1Password のボルトからシークレットを取得して環境変数として注入してくれるやつ。Doppler でも同じアプローチが使えます。

セットアップ手順

# 1. direnv のインストール
brew install direnv          # macOS
sudo apt install direnv      # Linux

# 2. シェルにフックを追加(zsh の場合)
echo 'eval "$(direnv hook zsh)"' >> ~/.zshrc
source ~/.zshrc

各プロジェクトルートに .envrc を作成します。既存の .env は削除して、.env.example(プレースホルダーのみ)だけ残す運用です。

# .envrc
export OPENAI_API_KEY=$(op read op://Private/OpenAI/api-key)
export STRIPE_SECRET_KEY=$(op read op://Private/Stripe/secret-key)
export DATABASE_URL=$(op read op://Private/DB/connection-string)
# direnv を有効化
direnv allow

# AI ツールの起動
op run -- cursor .        # Cursor
op run -- claude .        # Claude Code
doppler run -- cursor .   # Doppler の場合

なぜこの構成が効くのか

  • ディスクに平文キーが存在しない: AI が cat .env しても op://... という参照文字列しか出てこない
  • direnv のディレクトリスコープ: 他プロジェクトの環境変数が物理的に混入しない
  • 1Password CLI の出力制御: Filip Hric 氏の検証によると、console.log(process.env.KEY) を実行しても CLI が参照文字列を返すので、ターミナルに実際の値が表示されない

参考:Filip Hric - Don't let A.I. read your .env files

注意点

  • Windows では direnv のネイティブ対応が弱いので、WSL 経由がおすすめ
  • 初回セットアップ は10〜15分程度。一度やれば全プロジェクトで再利用できる
  • コスト は 1Password 個人プラン月$2.99〜、Doppler は個人無料枠あり

Tier 1 の限界

Hannecke 氏が指摘している通り、op run で注入されたシークレットはプロセスの環境変数として子プロセスに継承されます。Claude Code がテストを実行した場合、テストランナー経由で実際の値が見える可能性は残る。

対策としては、ローカル開発・テスト環境では本番キーを使わず、テスト専用のキーを使うのが現実的。Stripe なら test mode のキー、DB ならローカルのテスト用パスワードに限定しておけば、仮に漏洩しても実害ゼロにできます。

Tier 2:ツール別のファイル読み取りブロック

Tier 1 と併用して防御の層を増やします。

Claude Code

~/.claude/settings.json(グローバル設定)に deny ルールを追加。

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

ただし前述の通り、deny ルールが効かないバグが過去にあった。より信頼性の高い方法として、PreToolUse フックがおすすめです。GitHub Issue #6699 の報告者もこの方法を推奨しています。

フックは Claude Code がツールを実行する前に任意のスクリプトを走らせる仕組みで、exit 2 を返すとツール呼び出し自体がブロックされます。

// .claude/settings.json(プロジェクトルート)
{
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "Read|Edit|MultiEdit|Write",
        "hooks": [
          {
            "type": "command",
            "command": "$CLAUDE_PROJECT_DIR/.claude/hooks/protect-env.sh"
          }
        ]
      }
    ]
  }
}
#!/usr/bin/env bash
# .claude/hooks/protect-env.sh(chmod +x すること)
set -euo pipefail

file=$(jq -r '.tool_input.file_path // .tool_input.path // ""')

deny_patterns=(".env" ".env.*" "*.pem" "*.key" "credentials.json")

for pattern in "${deny_patterns[@]}"; do
  if [[ "$(basename "$file")" == $pattern ]]; then
    echo "Access to '$file' is blocked by security policy." >&2
    exit 2
  fi
done

exit 0

参考:Claude Code 公式 - Hooks Guide

Cursor

リポジトリルートに .cursorignore を作成。

**/.env
**/.env.*
**/secrets/

加えて .cursorrules に以下を書いておくと、エージェントモードでの参照も抑制できます。

Never reference paths outside this project root.
Never read or output .env file contents.

GitHub Copilot(VS Code)

// settings.json
{
  "github.copilot.ignoredFiles": ["**/.env", "**/.env.*"]
}

Tier 3:サンドボックスによるプロセス隔離

AI ツール自体を隔離された環境で実行して、ホストマシンのファイルシステムへのアクセスを遮断するアプローチ。Tier 1・2 がシークレットの保護に焦点を当てているのに対して、Tier 3 は AI エージェントの行動範囲そのものを制限します。

Docker Sandboxes

Docker 社が 2026年1月にリリースした、AIコーディングエージェント向けのサンドボックス環境。microVM ベースの隔離を提供していて、Claude Code、Codex CLI、Copilot CLI、Gemini CLI に対応しています。

ファイルシステムが完全に分離されるので、ホスト上の他プロジェクトの .env はそもそも見えない。--dangerously-skip-permissions フラグを安全に使えるようになるのも地味にありがたい。

参考:Docker Blog - Docker Sandboxes: Run Claude Code and More Safely

DevContainer

Andrea Bizzotto 氏が公開している DevContainer 構成を使えば、VS Code / Cursor / Claude Code の組み込みターミナルから隔離環境を利用できます。コンテナ内の /workspace にプロジェクトがマウントされ、ホストの他ディレクトリには触れられない。

参考:Andrea Bizzotto - How to Safely Run AI Agents Inside a DevContainer

Bubblewrap(Linux)

Docker を使わずに軽量なサンドボックスを構築する方法。Patrick McCanna 氏がブログで詳しく解説しています。Anthropic 自身も Claude Code 内部で Bubblewrap を利用しているけど、McCanna 氏は「ベンダーの実装に依存せず自前で制御するほうが確実」という立場です。

参考:Patrick McCanna - A better way to limit Claude Code access to Secrets

ExitBox

Cloud Exit 社が 2026年2月にリリースしたオープンソースのサンドボックス。AES-256 で暗号化されたシークレット vault を内蔵していて、エージェントがシークレットにアクセスするたびに承認ポップアップが表示されます。.env ファイルはコンテナ内で /dev/null にマウントされるので、物理的に存在しない扱い(やりすぎ感あるけど確実ではある)。

参考:Cloud Exit - Introducing ExitBox

macOS での選択肢

Infralovers 社が 2026年2月に公開した記事で、macOS におけるサンドボックスの選択肢が網羅的に比較されています。Lima(CNCF プロジェクト)で Linux VM を立てて Claude Code を動かすパターンが紹介されていて、「read 制限のほうが write 制限より重要」「~/.ssh を読まれてネットワークアクセスがあれば即 exfiltration」という指摘はハッとさせられます。

参考:Infralovers - Sandboxing Claude Code on macOS

Tier 4:追加の保険レイヤー

Pre-commit hook + Gitleaks

コミット前に API キーらしき高エントロピー文字列を検知してブロックします。Tier 1〜3 をすり抜けた場合の最後の砦。

brew install gitleaks
gitleaks git -v --staged   # pre-commit hook に追加

テスト出力のマスク

Claude Code はターミナルに表示された内容をキャプチャします。テスト出力をファイルにリダイレクトするだけでもリスクは下がる。

pytest --tb=short --no-header          # 出力を簡潔に
npm test > /tmp/test-output.log 2>&1   # ファイルにリダイレクト

Anthropic 側の動き

2026年3月1日、Claude Code の GitHub Issues に「ビルトインのシークレット管理機能」の提案が投稿されています(Issue #29910)。チャットのコンテキストにシークレットが混入しない専用 UI や、プロジェクトレベルのシークレット参照機能が議論されていて、エンジニア以外のユーザーが Claude Code を使い始めている現状を踏まえた提案です。

ツール側の改善は今後進みそうだけど、2026年3月時点ではユーザー側の多層防御が主流。

まとめ

.claudeignore.cursorignore は第一防衛線として有効だけど、deny ルールのバグやエージェントモードでの bash バイパスを考えると、これだけに頼るのはリスクがあります。

2026年3月時点で最も支持されている構成はこの3つの組み合わせです。

  1. direnv + 1Password CLI でディスクから平文キーを排除(根本対策)
  2. ツール別の deny / フック設定でファイル読み取りをブロック(即効対策)
  3. 必要に応じてサンドボックスで隔離(追加防御)

僕自身、Codex に勝手に API キーを使われた件をきっかけにこの構成に移行しました。正直、最初は「direnv + 1Password CLI のセットアップ面倒だな...」と思ったけど、15分で終わるし、一度やれば全プロジェクトで使い回せるのでかなりコスパいいです。

OS や使用ツールによって設定の詳細は異なるので、「うちの環境だとこうしてるよ」みたいな情報があればコメントで教えてもらえると助かります。


本記事は2026年3月7日時点の情報に基づいています。ツールの仕様は変更される可能性があるため、公式ドキュメントも併せて確認してください。

Discussion