← ブログに戻る

「とりあえず全部許可」でClaude Codeを動かすと、.envの秘密がそのままAnthropicに渡る話

claudecodesecurityaiagents

最初に範囲を区切らせてください。この記事は、GitHubのコミット偽装でもなく、エディタ拡張のサプライチェーン汚染でもありません。話すのは一点だけ、エージェントを動かしている最中に、.env の中身がツール出力に乗ってLLMプロバイダへ渡る経路です。コードを書く本人が一番油断している場所、と言い換えてもいいです。

私も最初は油断していました。Claude Codeを auto 寄りの権限で回して、ターミナルに流れる出力をろくに見ていませんでした。便利だったからです。便利なものは、たいてい後ろから刺さります。

「疑わしきも許可」が秘密を運ぶ仕組み

問題はファイルを直接開く話だけではありません。エージェントが実行したコマンドの出力が、そのまま会話コンテキストに取り込まれ、APIに送られます。これが見落とされがちな漏洩経路です。

具体的にはこうです。エージェントがデバッグのために printenv を打つ。あるいはアプリの起動ログに環境変数が出る。docker inspect の結果に認証情報が混ざる。そのテキストは全部、Claude Codeのコンテキストに入ります。そしてコンテキストはAnthropicのAPIに送られる。AWS_SECRET_ACCESS_KEY が、誰も「漏らそう」と思っていないのに、平文で旅に出るわけです。

OWASPは2026年に「Agentic Security Top 10」を公開しました。その A2(過剰な権限付与)と A4(データ漏洩)は、図解の中の脅威ではなく、auto--dangerously-skip-permissions を押した瞬間に現実になります。GitGuardianの2026年レポートでは、公開GitHub上で2,900万件のシークレットが検出され、AI支援のコミットはベースラインの約2倍の頻度で秘密を漏らしていた、というデータも出ています。エージェントは秘密を漏らすのが上手いのです。悪意なく、勤勉に。

疑わしきも許可の運用で、コマンド出力経由の秘密がLLMプロバイダへ渡る漏洩経路の図

deny-rules で危険なパターンを明示的に止める

最初の防御線は .claude/settings.json の deny ルールです。deny は allow より常に優先されるので、危険なパターンをここで名指しで潰します。

{
  "permissions": {
    "allow": [
      "Read",
      "Bash(npm test *)",
      "Edit(src/**/*.ts)"
    ],
    "deny": [
      "Read(.env)",
      "Read(.env.*)",
      "Edit(*.env*)",
      "Write(*.env*)",
      "Read(*.pem)",
      "Read(*.key)",
      "Read(credentials.json)",
      "Bash(curl * | bash)"
    ]
  }
}

設定の優先順位も押さえておくと効きます。管理ポリシー(/etc/claude-code/settings.json)はユーザーが上書きできないので、チームで強制したいルールはここに置きます。個人の油断を、組織の設定で先回りして潰すわけです。

deny-rules を過信してはいけない理由

ここで正直に書きます。deny ルールだけを信じるのは危険です。2026年に入って、Read の deny ルールが .env に対して実際には効いていない、という不具合が複数報告されました(anthropics/claude-code の Issue #24846 ほか)。deny に .env を入れて「守った」と思い込んだ状態が一番こわい。プロンプトも警告もなく、エージェントが拒否したはずのファイルを読んでいた、という報告です。

つまり deny-rules は「最初の壁」であって「最後の砦」ではありません。鍵をかけたつもりのドアが、たまにノブだけ回ると思っておいたほうがいい。だから層を重ねます。

多層で守る: 設定の外側に防御を置く

秘密を守る一番確実な方法は、そもそもディスク上に平文の秘密を置かないことです。実行時に取得して、ファイルに書かないなら、どんなツールにも読むものがありません。

私が実際にやっている順番はこうです。

  1. .env をディスクから減らす。 本番に近い値はシークレットマネージャ(AWS Secrets Manager、HashiCorp Vault)に寄せ、実行時に注入する。ローカルの .env はダミー値に置き換える。漏れて困らない値なら、漏れても困りません。
  2. .gitignore と除外設定を二重で張る。 .env*.pem*.keycredentials.json をコミット対象から外し、エージェントの除外設定にも入れる。
  3. Hooks で二重チェックする。 権限ルールに加えて、Hooks で危険なツール使用を検査し、Exit code 2 でブロックする。async オプションを使えば、すべてのツール使用を外部ログに記録して後から監査できます。
  4. APIキーを最小権限+月間上限にする。 エージェント用のキーは、必要最小限の権限と利用上限を設定する。万一漏れても、被害の天井を低くしておく。
  5. ネットワークを断つ選択肢を持つ。 ローカルファイルだけを触るタスクなら、Dockerコンテナを --network none で起動して、外への送信経路ごと塞ぐ。出ていけないなら、漏れようがありません。

deny-rulesを内側の壁、シークレットマネージャ・gitignore・Hooks・ネットワーク遮断を外側の層とする多層防御の図

5つ全部を最初からやる必要はありません。私のおすすめは、まず 1 と 2 だけ今日やることです。.env をダミーに差し替えて .gitignore を確認する。これだけで、漏れて致命傷になる平文の秘密がローカルから消えます。残りは運用の本気度に合わせて足していけばいい。

エージェント実行時権限は「設定して終わり」ではない

セキュリティは設定ファイルに書いた瞬間に完成、ではありません。CVE情報やOWASPの更新、そして今回の deny-rules の不具合のように、前提が静かに変わります。auto の便利さは本物です。ただ、便利だからといって出力を見なくていいわけではありません。私が一番痛い目を見たのも、ターミナルを見ていなかった時でした。

権限を最小化し、deny で危険を名指しし、その外側に「平文の秘密を置かない」「出口を塞ぐ」を重ねる。エージェント実行時の秘密漏洩は、この多層でほぼ止まります。完璧な一枚の壁を探すより、そこそこの壁を何枚か立てるほうが現実的です。一枚くらい錆びていても、後ろにまだ何枚か残っています。ターミナルを見ていなかった私が、それでも今は安心してエージェントに仕事を任せられているのは、そのおかげです。


Claude Codeの権限モデル・サンドボックス・コスト設計をまとめて知りたい方は、書籍にしました。

Claude Code Mastery