Claude Codeの「今後確認しない」で秘密情報1,379件がGitHubに流出 — settings.local.jsonの仕組みと対策
はじめに
Claude Code の許可プロンプトで「Yes, and don’t ask again(今後確認しない)」を選んだコマンドが、パスワードやトークンごと公開 GitHub リポジトリに流出していた — セキュリティ企業 Apiiro が 11 月 7 日にこんな調査を公開し、Hacker News で 500 ポイントを超える話題になりました。
この記事では、なぜ「許可しただけ」で秘密情報がファイルに残るのか、Claude Code の gitignore の仕組みのどこに穴があるのか、そして利用者・組織が今すぐできる対策を整理します。
何が起きたのか — 1,122 ファイル・1,379 件の秘密情報
日本語の解説記事(Dev & Gadget Note)によると、Apiiro の調査の概要は次のとおりです。
- 公開 GitHub 上の
.claude/settings.local.json104,185 件を調べ、1,122 件に少なくとも 1 つの秘密情報が含まれていた(合計 1,379 件) - 過去のバージョン、フォーク、共有設定、エージェントの会話ログまで含めると、1,994 リポジトリで 2,539 件
- 内訳はデータベースのパスワード 379 件、公開サーバーへの SSH ログイン 43 件、GitHub / GitLab / npm トークン 42 件、AWS キー 23 件など
- 影響を受けた組織は 63。通知を受けた企業の 1 社は、キーがまだ有効だったことを認めた
- 影響リポジトリの半数では、秘密情報が 217 日以上デフォルトブランチに残っていた
注目すべきは、既存のシークレットスキャナーが半分近くを見逃していた点です。gitleaks が検出したのは 1,122 件中 564 件、TruffleHog は 643 件で、215 件はどちらにも引っかかりませんでした。見逃しの多くは PGPASSWORD=...、sshpass -p ...、mysql -p... のようなコマンドライン引数のパスワードで、AWS キーのような決まった形式を持たないため、パターン照合では拾えません。
なぜ起きるのか — 「許可」がそのまま平文の設定になる
「今後確認しない」はコマンドをそのまま保存する
公式ドキュメントには次のように書かれています。
When you choose “Yes, and don’t ask again” and the approval saves permanently, such as for a Bash command or a WebFetch domain, Claude Code saves the rule to
.claude/settings.local.jsonat the root of the git repository
保存されるのは permissions.allow の配列に入る Bash(...) 形式のルールです。npm test * のようなパターンなら問題ありませんが、一度きりのコマンドを許可すると、その文字列がそのままルールになります。たとえばモデルが接続確認のために次のようなコマンドを組み立て、それを「今後確認しない」で許可すると、パスワードが平文で設定ファイルに書かれます。
{
"permissions": {
"allow": [
"Bash(PGPASSWORD=s3cr3t psql -h db.example.com -U admin -c 'select 1')"
]
}
}
人間が書いたコマンドなら秘密情報を直書きしない人でも、エージェントが組み立てたコマンドを流れで承認すると、何がファイルに残るかを意識しにくくなります。ここが従来の「.env をうっかりコミット」と違う点です。
gitignore は「自分の PC だけ」に効く
Claude Code はこのファイルを守る仕組みを持っています。ただし公式ドキュメントによると、その方法はプロジェクトの .gitignore ではなく、グローバルな git 除外ファイルへの追記です。
it adds
**/.claude/settings.local.jsonto your global git excludes file, so the file stays out of your commits in every repository.
書き込み先は core.excludesFile、未設定なら ~/.config/git/ignore です。この方式には次の抜け道があります。
- 別の PC・コンテナ・CI 環境にはグローバル設定がないため、無視されない
- 手で作ったファイルは対象外(ドキュメントも「自分で
.gitignoreに追加して」と書いている) - 一度追跡されたファイルには ignore が効かない
Hacker News では Claude Code チームの Boris Cherny 氏が「Claude が作成したときに自動で gitignore しているので、今回のケースは手動で作られてコミットされたもの」と説明しました。一方、記事の著者は「グローバルに追加するだけでリポジトリの .gitignore ではない」と反論し、新しいマシンやコピーしたフォルダで漏れる経路を挙げています。解説記事によると、追跡できた 1,105 リポジトリのうち 539 件では Claude が共著者のコミットでこのファイルが変更されていたとのことで、「人間のミス」だけで片付けられる話ではありません。
Anthropic の反応と議論
Cherny 氏は「これまで Claude Code は秘密情報の除去をしてこなかった(そのための良いツールは多い)が、防ぐ方法を検討している」と述べ、auto モードでの検出や簡易なシークレットスキャンの組み込みを試作すると書いています。あわせて、権限ルールよりも分類器が操作を審査する auto モードの利用を勧めました。
これに対しては、「決定的なルールで制御したい」「ツールは初期状態で安全であるべきで、使い方が悪いという回答は筋違い」といった反発が目立ちました。解説記事は、2025 年 8 月の時点で同じ問題がバグ報告されており、関連する 4 件が bot によってクローズされていたとも伝えています。
エンジニアが今すぐできる対策
個人の利用者向け
- 手元の
.claude/settings.local.jsonを開いて中身を確認する。コミットしていなくても、秘密情報を含むルールは削除する - プロジェクトの
.gitignoreに.claude/settings.local.jsonを追加する。既に追跡済みならgit rm --cached .claude/settings.local.jsonで追跡を外す - 許可はパターンで行う。
Bash(npm run *)のように、コマンドの「種類」を許可する。秘密情報を含む一回限りのコマンドは「Yes」(今回だけ)で通す - 秘密情報はコマンドに書かせない。
$DATABASE_URLのように環境変数やシークレットマネージャー経由で渡す
流出が疑われる場合・組織向け
- 先にキーをローテーションする。ファイルを消しても git 履歴やフォークに残るため、削除だけでは止まらない
- 組織の GitHub で
.claude/settings.local.jsonと.claude/settings.jsonを検索し、履歴まで確認する .claude/、.cursor/、.gemini/などのエージェント用フォルダを.envと同じ扱いにし、組織の.gitignoreテンプレートに入れる- pre-commit と CI のシークレットスキャンを有効にする。ただし今回の結果のとおり、コマンドライン引数のパスワードはスキャナーをすり抜けやすいので、
-p、PASSWORD=などのパターンを独自ルールで補う
まとめ
- 「Yes, and don’t ask again」は、許可したコマンドを文字列のまま
.claude/settings.local.jsonに保存する - Claude Code の自動除外はグローバル git 設定への追記で、別環境や追跡済みファイルには効かない
- 秘密情報を含むファイルの約 2 割(1,122 件中 215 件)は gitleaks・TruffleHog の両方をすり抜けた
- 対策は「プロジェクトの
.gitignoreに追加」「パターンで許可」「秘密情報をコマンドに書かせない」「流出時はまずローテーション」
エージェントの設定ファイルは、もはや「ただの設定」ではなく、作業の履歴そのものです。Anthropic がシークレットスキャンの組み込みに踏み込むかどうかが、次の注目点になります。
ソース
- Apiiro: Claude Code’s “Don’t Ask Again” exposes passwords in 1,122 public GitHub files(※執筆時に本文へアクセスできなかったため、数値は下記の日本語解説記事と Hacker News の議論を参照)
- Dev & Gadget Note: Claude Codeの「今後確認しない」が秘密情報をGitHubに漏らす
- Hacker News: ディスカッション
- Claude Code Docs: Settings
- Claude Code Docs: Configure permissions