結論:Claude Codeにコードを触らせる時代、秘密情報(APIキー・DB接続情報・トークン)を守る主戦場は「AIエージェントに何を読ませ、何を実行させるか」の権限設計に移りました。守りたいルールは注意書きではなく設定で強制するのが鉄則です。
- 要点1:.envや認証情報ファイルは、permissionsのdenyで「Claude Codeに読ませない」を仕組みとして強制する。CLAUDE.mdへの「読むな」というお願いでは守れない
- 要点2:秘密情報はコードに埋めず環境変数へ。Claude Code自身も環境変数・apiKeyHelper・direnvで安全に鍵を受け渡せる
- 要点3:流出検知はgitleaksをフックとCIに二重で仕込む。claude -p(ヘッドレスモード)で既存コードのハードコード秘密を棚卸しできる
対象読者:Claude Codeをチームや個人で使う開発者・SRE・セキュリティ担当。.envと.gitignoreの基本がわかる方を想定しています。
今日やること:まず.claude/settings.jsonのpermissions.denyにRead(./.env)を1行足し、リポジトリのルートでgitleaks gitを一度走らせて、既に履歴へ紛れ込んだ秘密がないかをスキャンしてみてください。
APIキーをAIに「見せてしまう」新しいリスク
正直に告白すると、筆者はClaude Codeを使い始めた最初の週に、ヒヤリとする体験をしました。あるプロジェクトで「このアプリの設定周りを整理して」と雑に頼んだところ、Claude Codeは当然のようにプロジェクト直下の.envを読み込み、その内容を前提に作業を進めようとしたんです。中には本番のAPIキーが入っていました。実害はありませんでしたが、「AIエージェントは、人間が無意識に避けているファイルにも平気で手を伸ばす」という当たり前の事実を、身をもって理解した瞬間でした。
従来の秘密情報管理は「人間の運用ルール」で回っていました。.envは.gitignoreに入れる、キーはコミットしない、Slackに貼らない——これらは全部、人間の注意力に依存した約束事です。ところがコードを読み・コマンドを実行し・ファイルを書き換えるエージェント型AIが加わると、前提が変わります。AIは.envの中身をモデルのコンテキストに送るかもしれないし、生成したコードにキーをハードコードするかもしれないし、うっかりgit add .相当の操作で秘密をステージするかもしれない。秘密情報の攻撃面(アタックサーフェス)が、人間の手作業から「AIの自動実行」へと広がった——これが本記事の出発点です。
本記事では、Claude Codeを使いながら秘密情報を守るための実践手法を、権限設計から暗号化管理・CI連携まで、7つの原則にまとめて解説します。扱うのは「AIに触らせない設計」と「そもそもコードに秘密を置かない設計」の両輪です。権限そのものの考え方は権限設計ガイドで体系的に整理しているので、本記事はその秘密情報特化版として読んでください。
前提:この記事の動作環境と「7原則」の全体像
まず本記事の検証環境と、これから解説する7原則の地図を示します。個別のコマンドや挙動は以下の環境での筆者の確認に基づくもので、バージョンや構成によって結果は変わります。
| 項目 | バージョン・構成 |
|---|---|
| Claude Code | 2026年7月時点の最新版(npm @anthropic-ai/claude-code 経由) |
| OS | macOS / Linux(WSL2含む) |
| 秘密検知 | gitleaks v8系 / git-secrets(awslabs) |
| 暗号化 | SOPS + age(getsops) |
| 環境変数管理 | direnv |
本記事で解説する「秘密情報を守る7原則」は次のとおりです。以降の章がそれぞれの原則に対応します。
| # | 原則 | 効く場面 |
|---|---|---|
| 1 | 読ませない:秘密ファイルはpermissions.denyで封じる | AIによる意図しない読み取り・コンテキスト送信 |
| 2 | 埋めない:秘密はコードでなく環境変数から渡す | ハードコードによる流出 |
| 3 | 鍵を安全に渡す:apiKeyHelperとdirenvを使う | Claude Code自身・ツールの認証情報管理 |
| 4 | 追跡させない:.gitignoreと.env.exampleで境界を引く | Gitへの混入 |
| 5 | 検知する:gitleaksをフックとCIに二重化する | 混入した秘密の早期発見 |
| 6 | 暗号化して載せる:SOPS+ageでGit管理を安全にする | 設定と秘密を一緒に配布したい場面 |
| 7 | 棚卸しする:既存コードの秘密をAIで洗い出す | レガシーコードの負債返済 |
原則1:読ませない — 秘密ファイルをpermissions.denyで封じる
機能の話より先に、権限の話をさせてください。ここが秘密情報管理の一丁目一番地です。
まず正確に押さえておきたい事実として、Claude Codeは既定で.envを保護しません。「秘密っぽいファイルだから自動で避けてくれるだろう」は成り立たないんです(公式ドキュメント「Configure permissions」を確認する限り、.envの既定deny挙動は明記されていません)。さらに、Claude CodeはプロジェクトのRead可否を.gitignoreで自動判定するわけでもありません。ファイルの読み書きを制御するのは、あくまでpermissionsのルールです。だからこそ、読ませたくないファイルは自分でdenyに書く必要があります。
Claude Codeのpermissionsは、ツール実行を「allow(自動許可)/ ask(都度確認)/ deny(拒否)」の3段階で制御でき、denyはallowより常に優先されます(公式ドキュメント「Configure permissions」より)。ファイル読み取りにはRead(パターン)ルールが使え、パターンは.gitignore同様のグロブ記法(*は同一階層、**は階層横断)で書けます。筆者がプロジェクトの.claude/settings.jsonに入れている最小構成はこうです。
{
"permissions": {
"deny": [
"Read(./.env)",
"Read(./.env.*)",
"Read(./**/.env)",
"Read(./secrets/**)",
"Read(./**/*.pem)",
"Read(./**/id_rsa)",
"Read(~/.ssh/**)",
"Read(~/.aws/credentials)"
]
}
}
これで、Claude Codeは.envや鍵ファイルの中身をそもそも読めなくなります。読めなければ、モデルのコンテキストに秘密が乗ることも、生成コードにキーが写経されることもない。「CLAUDE.mdに『.envは読むな』と書けばいいのでは?」と思うかもしれませんが、CLAUDE.mdの指示は行動の方向付けであって強制力はありません。強制力を持つのはpermissionsとフックです。守らせたいルールは設定側に書く——これはsettings.json設定ガイドでも繰り返し出てくる原則で、秘密情報こそ最優先で適用すべき対象です。
注意点として、.env.exampleのような「秘密を含まないテンプレート」まで巻き込んでdenyすると、Claude Codeが必要な環境変数のキー名を把握できず不便です。denyは.env本体と.env.local等の実値ファイルに絞り、.env.exampleは読ませる。この線引きが実用上のコツです。
原則2:埋めない — 秘密はコードでなく環境変数から渡す
読ませない設計と対になるのが、「そもそもコードに秘密を書かない」設計です。AIに触らせる・触らせない以前に、秘密がソースコードの中に平文で存在すること自体が最大のリスクだからです。
原則はシンプルで、秘密は環境変数から読む。ハードコードされた文字列リテラルではなく、process.env.API_KEYやos.environ["API_KEY"]のように実行時に注入する形にします。Claude Codeにコードを書かせるときも、この方針をCLAUDE.mdに明記しておくと、生成コードが最初から環境変数参照になります。筆者のCLAUDE.mdにはこう書いています。
# CLAUDE.md(秘密情報の扱い・抜粋)
## 秘密情報のルール
- APIキー・トークン・接続文字列をコードに直書きしない
- 必ず環境変数から読む(例: process.env.X / os.environ["X"])
- 新しい秘密が必要になったら、.env.example にキー名だけ追記し、
実値はコミットしないこと(実値は人間が .env に手で入れる)
- ログやコンソール出力に秘密の値を出さない(マスクする)
ここで一つ、はまりやすい落とし穴があります。Claude Code自身のsettings.jsonには環境変数を注入するenvブロックがあり、セッションのサブプロセスへ環境変数を渡せます(公式ドキュメント「Settings」より)。便利なのですが、settings.jsonはリポジトリにコミットされる前提のファイルなので、ここに本番のAPIキーを直書きするのは絶対にNGです。envブロックに置いていいのは非機密の設定値(フラグやエンドポイントURL)まで。実際の秘密は、次章のapiKeyHelperやdirenvで渡します。この使い分けを外すと、秘密管理のためのファイルが秘密の流出源になるという本末転倒が起きます。
原則3:鍵を安全に渡す — apiKeyHelperとdirenvを使い分ける
秘密をコードから追い出すと、次は「では実行時にどう渡すか」が課題になります。ここはClaude Code自身の認証と、アプリの認証で、道具を分けて考えると整理できます。
Claude Code自身のAPIキー:apiKeyHelperで動的発行
Claude CodeにAPIキーを渡すとき、環境変数ANTHROPIC_API_KEYに長期キーをベタ置きするのが一番安直ですが、キーがシェル履歴やプロセス一覧に残るのが気持ち悪い。ここで使えるのがapiKeyHelper設定です。これは「認証情報を生成するシェルコマンドのパス」を指定するもので、Claude Codeはそのコマンドを実行して得た値をAuthorization: Bearerヘッダとして使います(公式ドキュメント「Settings」より)。CLAUDE_CODE_API_KEY_HELPER_TTL_MSで再取得の間隔も制御できます。
{
"apiKeyHelper": "/usr/local/bin/get-anthropic-key.sh"
}
このget-anthropic-key.shの中で、社内の秘密管理基盤(Vaultやクラウドのシークレットマネージャ)から短命トークンを取得して標準出力に返す、という運用にすれば、長期キーをファイルに置かずに済みます。企業導入でキー管理を厳格にしたい場合の定石で、法人導入セキュリティチェックリストでもこの動的発行を推奨構成として挙げています。
アプリの秘密:direnvでディレクトリ単位に読み込む
アプリ側が使う秘密(DBパスワード、外部APIキー等)は、direnvで「そのディレクトリに入ったときだけ環境変数が読み込まれる」形にすると扱いが楽です。direnvはディレクトリ単位で環境変数をロード/アンロードするツールで、.envrcに定義を書きます。
# .envrc(direnv用・これ自体はコミットしてよいが実値は書かない)
# 実値は .env(gitignore対象)から読み込む
dotenv .env
# あるいは秘密管理基盤から動的に取得
export DB_PASSWORD="$(vault kv get -field=password secret/myapp/db)"
direnvを使うと、ターミナルでそのプロジェクトにcdした瞬間に必要な環境変数が揃い、離れると消えます。Claude Codeが実行するBashコマンドもこの環境変数を引き継げるので、「アプリは動くが、秘密はファイルにもコードにも残っていない」状態を作れます。.envrcにvaultコマンド経由の動的取得を書けば、平文の秘密ファイルを完全になくすことも可能です。
【ここまでの権限・鍵の設計を自社に定着させたい方へ】 permissionsのdeny設計やapiKeyHelperと社内シークレット基盤の連携は、既存のセキュリティ運用との擦り合わせが要になります。UravationではClaude Code個別指導・導入支援として、開発チーム・セキュリティ部門向けに、この記事の設計を自社の秘密管理ポリシーに落とし込む伴走をしています。「うちのVault構成だとapiKeyHelperをどう書くか」といった個別相談から可能です。
原則4:追跡させない — .gitignoreと.env.exampleで境界を引く
権限と環境変数で「今この瞬間」の秘密は守れます。次に守るべきは時間軸——Gitの履歴に秘密を刻んでしまわないことです。一度コミットされた秘密は、その後削除しても履歴に残り続け、リポジトリを取得できる全員が過去を掘れば読めてしまいます。
基本の三点セットはこうです。
- .gitignore:
.env、.env.local、*.pem、secrets/など秘密を含むパスを追跡対象から外す - .env.example:キー名だけ書いた雛形をコミットし、チームで必要な環境変数を共有する(実値は空)
- コミット前スキャン:混入を人手に頼らず、次章のgitleaksで機械的に止める
# .gitignore(秘密関連の抜粋)
.env
.env.*
!.env.example
*.pem
*.key
id_rsa
secrets/
**/credentials.json
ここで注意したいのが、前述のとおりClaude Codeは.gitignoreを読み取り制御に自動連動させない点です。.gitignoreはあくまでGitの追跡対象を決めるもの。AIに読ませない制御は原則1のpermissions.denyで別途かける必要があります。つまり.envは「.gitignoreでGitから隠す」「permissions.denyでAIから隠す」の二重で守る。役割が違う二つの設定を、両方書くのが正解です。この分離を理解していないと、「.gitignoreに入れたから安心」と思い込んで、実はAIには丸見えだった、という穴が空きます。
原則5:検知する — gitleaksをフックとCIに二重で仕込む
人間もAIも、どれだけ気をつけても秘密の混入はゼロにできません。だから「混入しない」努力と並行して、「混入したら即座に気づく」仕組みを持ちます。ここで主役になるのがgitleaksです。
gitleaksは、リポジトリやファイルの中からパスワード・APIキー・トークンといった秘密を、設定可能なルールで検出するツールです。使いどころは大きく二つ、コミット前(pre-commit)とCIで、この二段構えが基本形になります。
コミット前フック:混入を手元で止める
まずローカルで、コミットしようとした変更に秘密が含まれていたらコミット自体を失敗させます。pre-commitフレームワーク経由が管理しやすいです。
# .pre-commit-config.yaml
repos:
- repo: https://github.com/gitleaks/gitleaks
rev: v8.x.x
hooks:
- id: gitleaks
これでgit commitのたびにgitleaksが差分をスキャンし、秘密らしき文字列を見つけたらコミットを止めます。AWS認証情報に特化した軽量な選択肢としてgit-secrets(awslabs)もあり、AWSキーの誤コミットだけを確実に防ぎたいならこちらも有効です。
Claude Codeのフックで、AI経由の危険操作も止める
もう一つ、Claude Code特有の防御があります。Claude CodeのPreToolUseフックは、ツール(Bashなど)が実行される直前に任意のスクリプトを差し込め、その結果でツール実行を止められます。具体的には、フックが終了コード2を返すか、permissionDecisionをdenyにしたJSONを出力すると、その操作はブロックされます(公式ドキュメント「Hooks」より)。これを使えば、「Claude Codeが実行しようとしたコマンドや書き込もうとした内容に秘密が含まれていないか」を実行前に検査できます。
#!/usr/bin/env bash
# .claude/hooks/pre-bash-secret-scan.sh
# PreToolUse (Bash) で、コマンド文字列に秘密パターンが無いか検査
input="$(cat)"
cmd="$(echo "$input" | jq -r '.tool_input.command // ""')"
# 秘密らしきパターン(例)を検出したらブロック
if echo "$cmd" | grep -Eiq '(sk-[a-z0-9]{20,}|AKIA[0-9A-Z]{16}|-----BEGIN [A-Z ]*PRIVATE KEY-----)'; then
echo '{"hookSpecificOutput":{"hookEventName":"PreToolUse","permissionDecision":"deny","permissionDecisionReason":"秘密らしき文字列を含むコマンドをブロックしました"}}'
exit 0
fi
exit 0
フックの登録方法や整形・通知系フックとの組み合わせはフック実践ガイドで詳しく扱っています。permissions.denyが「読ませない」静的な壁なら、フックは「実行の瞬間に中身を見て止める」動的な検問所。役割が違うので、両方かけると守りが厚くなります。
原則6:暗号化して載せる — SOPS+ageで設定と秘密を安全に配布する
「秘密は絶対にGitに入れない」が理想ですが、現実には「設定ファイルと一緒にバージョン管理したい」ニーズがあります。Kubernetesのマニフェスト、環境別の設定、CI用の値——これらを秘密込みでGitOps的に扱いたい。そこで登場するのが暗号化してからコミットするアプローチで、定番がSOPS+ageの組み合わせです。
SOPS(getsops)はYAML/JSON/ENVなどのファイルを暗号化・復号できるエディタで、値だけを暗号化してキー名は平文のまま残せるのが特徴です。暗号鍵にはage(モダンで扱いやすい暗号化ツール)やクラウドKMS(AWS KMS / GCP KMS)を使えます。差分レビューがしやすく、Gitの履歴に載せても中身が読めない状態を保てるのが利点です。
# age鍵を生成
age-keygen -o key.txt # 公開鍵が出力される(例: age1...)
# .sops.yaml で暗号化ルールを定義
# creation_rules:
# - path_regex: secrets/.*\.enc\.yaml$
# age: age1xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
# 値だけ暗号化して保存(キー名は平文で残る)
sops --encrypt secrets/prod.yaml > secrets/prod.enc.yaml
# 復号(復号鍵を持つ人・環境だけが読める)
sops --decrypt secrets/prod.enc.yaml
この運用なら、secrets/prod.enc.yamlはGitにコミットしてよくなります(中身は暗号化済み)。Claude Codeに設定を触らせる場合も、AIが読めるのは暗号文だけ。復号鍵はローカルや秘密管理基盤に置き、permissions.denyでRead(key.txt)やRead(~/.config/sops/**)を封じておけば、AIは構造を理解しつつ実値には触れない、というバランスが取れます。コンテナ開発環境ごと秘密の扱いを固めたい場合は、Dev Container安全導入の考え方と組み合わせると、チーム全員の環境で同じ守りを再現できます。
原則7:棚卸しする — 既存コードのハードコード秘密をAIで洗い出す
ここまでは「これから秘密を混入させない」話でした。最後の原則は逆方向、すでに混入しているかもしれない秘密を掘り起こす棚卸しです。歴史のあるリポジトリほど、過去のコミットや設定ファイルに秘密が眠っている確率が高い。ここでエージェント型AIの横断調査力が効きます。
gitleaksが「パターンマッチで機械的に拾う」のに対し、Claude Codeは「文脈を読んで怪しさを判断する」棚卸しができます。両方を組み合わせるのが最強です。まずgitleaksで全履歴をスキャンします。
# リポジトリの全コミット履歴を対象に秘密をスキャン
gitleaks git --report-path gitleaks-report.json
# 作業ツリー(未コミット含む)だけを対象にする場合
gitleaks dir .
そのうえで、Claude Codeに「パターンに引っかからないが危険なもの」を探させます。ヘッドレスモードで読み取り専用の棚卸しを走らせるのが安全です。
claude -p "このリポジトリを調査し、秘密情報の管理状況を棚卸ししてください。
観点:
1. ソースコードに直書きされたAPIキー・トークン・パスワード・接続文字列
2. 環境変数を使うべきなのにハードコードされている設定値
3. .gitignore されずにコミットされている .env や鍵ファイルの有無
4. ログ出力・エラーメッセージに秘密の値が出ている箇所
検出は重大度順のMarkdown表で。値そのものは伏せ字にし、
ファイルと行番号だけ示すこと。修正はまだしないこと。" \
--allowedTools "Read,Grep,Glob"
ポイントは--allowedToolsを読み取り系だけに絞ること、そして「値そのものは伏せ字にせよ」と指示することです。棚卸しレポートに生の秘密を書かせては本末転倒ですから。この静的解析的な使い方はSAST脆弱性対応や脆弱性対応ワークフローと地続きで、秘密の棚卸しはセキュリティ棚卸しの一部として定例化すると効きます。
なお、gitleaksが履歴から実キーを検出した場合、コードから消すだけでは不十分です。その鍵はすでに漏れたものとして即座にローテーション(無効化・再発行)するのが鉄則。履歴からの完全除去(git filter-repo等)は補助的な後始末であって、一次対応は必ずキーの失効です。この順序を間違えると、「掃除したから安心」と思っている間に漏れた鍵が使われます。
CI/CDでの秘密スキャン自動化 — 二人目の門番を置く
ローカルのフックは、すり抜けられます。--no-verifyを付ければpre-commitフックは飛ばせるし、フックを入れていないメンバーもいる。だから最後の砦としてCIに秘密スキャンを置きます。ローカルとCIの二人の門番がいれば、片方をすり抜けてももう片方で止まる。
GitHub Actionsでの構成例です。PRとpushに対してgitleaksを走らせ、秘密が見つかればCIを失敗させます。
name: secret-scan
on: [push, pull_request]
jobs:
gitleaks:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0 # 全履歴をスキャンするため
- name: Run gitleaks
uses: gitleaks/gitleaks-action@v2
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
さらに一歩進めるなら、Claude Codeのヘッドレスモードを「秘密管理の観点でのコードレビューゲート」として組み込めます。gitleaksがパターン検出を担い、claude -pが「環境変数を使うべき箇所の直書き」「ログへの秘密出力」といった、パターンでは拾いにくい設計上の問題を指摘する、という分業です。
- name: Install Claude Code
run: npm install -g @anthropic-ai/claude-code
- name: Secret-hygiene review
env:
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
run: |
git diff origin/${{ github.base_ref }}...HEAD > /tmp/diff.txt
claude -p "この差分を秘密情報衛生の観点でレビューせよ。
観点: ハードコードされた鍵/トークン、環境変数にすべき値の直書き、
ログへの秘密出力、.env等のコミット。値は伏せ字で、指摘は重大度順の表で。
問題なければ 'LGTM' とだけ出力。" \
--allowedTools "Read" < /tmp/diff.txt
--allowedToolsをReadだけに絞り、CI上でクラスタや外部への副作用を一切持たせないのが作法です。用途ごとに権限を最小で渡す考え方はCI/CDパイプライン自動化で扱った設計と同じ。まずはコメント型・非ブロッキングで信頼を積み、精度に納得してから「重大度highが1件でもあればCIを落とす」ブロッキングゲートへ昇格させる、という段階導入をおすすめします。
【要注意】よくある失敗パターン4つと回避策
筆者自身の失敗と、導入相談で繰り返し見てきたつまずきを4つにまとめます。
失敗1:.gitignoreに入れたから安心、とAIから隠せていない
❌ .envを.gitignoreに追加しただけで「秘密は守れた」と考え、Claude Codeには読み放題のまま作業させる
⭕ .gitignore(Gitから隠す)とpermissions.denyのRead(./.env)(AIから隠す)を両方書く
なぜ重要か:前述のとおりClaude Codeは.gitignoreを読み取り制御に自動連動させません。.gitignoreは「コミットされないこと」を保証するだけで、「AIが読まないこと」は一切保証しない。役割の違う二つの設定を混同すると、コミットはされないがAIのコンテキストには毎回秘密が乗る、という見えにくい穴が空きます。
失敗2:settings.jsonのenvブロックに本番キーを書く
❌ 環境変数を渡したいからと、コミット対象のsettings.jsonのenvに本番APIキーを直書きする
⭕ envブロックは非機密の設定値まで。実際の秘密はapiKeyHelperやdirenv、秘密管理基盤から渡す
なぜ重要か:秘密管理のための設定ファイルが、リポジトリ経由の流出源になります。「設定ファイルだから安全そう」という直感が罠で、コミットされるファイルはすべて公開前提で扱うのが原則。秘密は必ず「コミットされない経路」で注入します。
失敗3:漏れた鍵をコードから消して満足する
❌ gitleaksが検出した実キーをソースから削除し、コミットして一件落着とする
⭕ まず鍵を即ローテーション(無効化・再発行)し、そのうえで履歴の後始末をする
なぜ重要か:Gitの履歴は分散しています。一度pushされた鍵は、cloneした全員の手元とサーバに残り、削除コミットでは過去を消せません。漏れた瞬間にその鍵は「もう使ってはいけないもの」。一次対応は掃除ではなく失効です。順序を逆にすると、掃除している間に悪用されます。
失敗4:AIに秘密の棚卸しをさせたら、レポートに生の値が並んだ
❌「秘密が直書きされている箇所を一覧化して」とだけ頼み、検出結果に実キーがそのまま出力される
⭕ 「値は伏せ字にし、ファイルと行番号だけ示せ」「修正はするな」「読み取り系ツールだけ許可」を必ず指示する
なぜ重要か:棚卸しレポート自体が新たな秘密の集約ファイルになってしまうと、それがSlackやチケットに貼られて二次流出します。AIに秘密を扱わせるときは、出力にも秘密を出させない制御が要る。プロンプトの一文と--allowedToolsの絞り込みで、これは防げます。
導入ロードマップ — 個人の1行denyからチーム標準まで3フェーズ
全部を一度にやる必要はありません。筆者が支援先に示している標準的な進め方を、汎用化して示します(期間は目安で、チーム規模・既存の秘密管理成熟度によって変わります)。
| フェーズ | やること | ゴール |
|---|---|---|
| Phase 1(〜2週間) | 個人の.claude/settings.jsonにpermissions.denyで秘密ファイルを封じる。ローカルにgitleaksのpre-commitフックを入れる。既存リポにgitleaks git を一度走らせて現状把握 |
「AIに読ませない」「うっかりコミットしない」の最低ラインが個人環境で成立する |
| Phase 2(1〜2ヶ月) | プロジェクト共有のsettings.jsonとCLAUDE.mdに秘密ルールを明文化。CIにgitleaksゲートを追加。apiKeyHelper/direnvで鍵の渡し方をチーム標準化 | 秘密管理がチームの仕組みになり、新メンバーも自動的に守られる |
| Phase 3(2ヶ月〜) | SOPS+ageで暗号化した設定のGit管理を導入。claude -pの秘密衛生レビューゲートをブロッキング化。鍵ローテーション運用と棚卸しの定例化 | 秘密の混入・流出が「起きても検知・失効できる」耐性を持った運用になる |
強調したいのは、Phase 1のdeny1行を今日やることです。Read(./.env)を足すだけで、今日から「AIが.envを読む」事故は止まります。土台の小さな一歩が、後段すべての前提になります。
FAQ — シークレット管理×Claude Codeでよく聞かれる質問
Q1. permissions.denyでReadを拒否すれば、秘密は完全に守れますか?
「AIによる読み取り」という一経路は仕組みで塞げますが、それだけでは万全ではありません。ハードコードされた秘密(原則2)、Gitへの混入(原則4・5)、既存の負債(原則7)はdenyだけではカバーできない別レイヤーです。本記事が7原則の多層防御として構成されているのはそのためで、denyは強力な一枚目の壁、と捉えてください。
Q2. 小さな個人プロジェクトでもここまでやるべき?
最低ラインは、個人でもやる価値があります。具体的には「permissions.denyにRead(./.env)」「.gitignoreに.env」「gitleaksを一度走らせる」の3つ。ここまでは数分で、事故コストに比べれば圧倒的に安い保険です。SOPSやCIゲートはチーム・本番運用になってから段階的に足せば十分です。
Q3. gitleaksとtrufflehog、どちらを使えばいいですか?
どちらも秘密検知の定番で、まずはgitleaksが導入しやすくおすすめです。trufflehogは検出した認証情報が「実際に生きているか」の検証(validation)まで踏み込めるのが強みで、より広い範囲(ファイルシステムやログ、各種SaaS)を対象にしたい場合に候補になります。まずgitleaksでフックとCIを固め、対象を広げたくなったらtrufflehogを検討、という順序が現実的です。
Q4. Claude Codeに秘密を扱う社内ツールを作らせても大丈夫ですか?
作らせること自体は問題ありません。むしろ「秘密は環境変数から読む」「ログにマスクをかける」といった正しい実装を、CLAUDE.mdの規約に沿って一貫して書いてくれるのはAIの得意分野です。注意すべきは、生成物のレビュー時に本記事の失敗パターン(特に直書きとログ出力)を必ずチェックすること。作らせる工程より、レビューの観点を固めるほうが大事です。
Q5. チーム導入で最初につまずくポイントは?
技術ではなく「秘密管理ポリシーがそもそも言語化されていない」ことです。どのファイルが秘密か、鍵はどこから渡すか、漏れたら誰が何をするか——これらが暗黙知のままだと、settings.jsonにもCLAUDE.mdにも書けません。逆に言えば、Claude Code導入がポリシー明文化を強制してくれる。この副産物が、実はセキュリティ全体の底上げにつながります。
Q6. .envを読ませないと、Claude Codeが必要な環境変数を把握できず不便では?
ここで.env.exampleが効いてきます。実値を含む.envはpermissions.denyで封じつつ、キー名だけを書いた.env.exampleはdenyの対象から外して読ませる。すると、Claude Codeは「このアプリはDATABASE_URLとSTRIPE_SECRET_KEYを必要とする」という構造は理解でき、コード生成や設定作業に支障が出ません。必要なのはキーの名前であって値ではない——この分離が、利便性と安全性を両立させる勘所です。実値が要る作業(実際に接続してテストする等)は、原則3のdirenvで環境変数として注入すれば、ファイルを読ませずとも実行時に値が揃います。
まとめ — 「守らせたいことは設定に書く」に尽きる
最後に本記事の要点を整理します。
- Claude Codeは.envを既定で保護しない。秘密ファイルはpermissions.denyの
Read(...)で明示的に封じる(原則1) - 秘密はコードに埋めず環境変数から。settings.jsonのenvブロックに本番キーを書かない(原則2)
- Claude Code自身の鍵はapiKeyHelperで動的発行、アプリの鍵はdirenvでディレクトリ単位に(原則3)
- .gitignoreと.env.exampleで境界を引く。ただし.gitignoreはAIの読み取り制御には連動しない(原則4)
- gitleaksをpre-commitとCIに二重化。Claude CodeのPreToolUseフックで実行時の秘密混入も止める(原則5)
- 設定を秘密込みでGit管理したいならSOPS+ageで暗号化してから載せる(原則6)
- 既存コードの秘密はgitleaks+claude -pで棚卸しし、見つけた鍵は即ローテーション(原則7)
7つの原則を貫くのは、たった一つの思想です。守らせたいことは、お願いではなく設定に書く。AIエージェントは注意書きを守る道徳ではなく、設定という物理法則の中で動きます。だからこそ、秘密を守る境界線はCLAUDE.mdの文章ではなく、permissions・フック・.gitignore・暗号化という「仕組み」で引く。まずは.claude/settings.jsonにRead(./.env)を1行。そこから始めてください。
【秘密情報を守るClaude Code運用を本気で設計したい方へ】 permissions設計、apiKeyHelperと社内シークレット基盤の連携、CIゲートの構築は、既存のセキュリティポリシーとの整合が要になる領域です。UravationのClaude Code個別指導・導入支援では、開発・セキュリティチーム向けに、この記事の7原則を自社の秘密管理ルールへ落とし込む設計から定着までを伴走支援しています。「まず何から手を付けるべきか」の壁打ちだけでも歓迎です。
著者プロフィール
佐藤傑(さとう・すぐる)。株式会社Uravation代表取締役。早稲田大学法学部在学中に生成AIの可能性に魅了され、X(旧Twitter)で活用法を発信(@SuguruKun_ai、フォロワー約10万人)。100社以上の企業向けAI研修・導入支援を展開。著書『AIエージェント仕事術』(SBクリエイティブ)。
出典・参考資料
- Claude Code公式ドキュメント: Configure permissions(deny/allow・Readルール)
- Claude Code公式ドキュメント: Settings(envブロック・apiKeyHelper)
- Claude Code公式ドキュメント: Hooks(PreToolUseによるブロック)
- Claude Code公式ドキュメント: Run Claude Code programmatically(ヘッドレスモード・–allowedTools)
- gitleaks(GitHub公式リポジトリ・秘密検知ツール)
- git-secrets(awslabs・AWS認証情報の誤コミット防止)
- TruffleHog(trufflesecurity・秘密検知と有効性検証)
- SOPS(getsops・ファイル暗号化) / age(暗号化ツール)
- direnv公式サイト(ディレクトリ単位の環境変数管理)