はじめに
先日、こちらの記事が話題になりました。
要約すると、AIエージェントがローカルファイルを読み書きできる時代、.envに平文で置いた秘密情報はAIから丸見えという問題提起です。
.gitignoreはGitへの流出を防ぎますが、AIエージェントからのアクセスは防げません。
では、Claude Codeではどう対策するのか?
この記事では、Claude Codeが備えているセキュリティ機能を5つの防御層として整理し、実践的な設定方法を解説します。
前提:何が問題なのか
従来の開発環境では、.envファイルに秘密情報を置くのが一般的でした。
# .env
DATABASE_URL=postgres://user:password@localhost:5432/mydb
API_KEY=sk-1234567890abcdef
.gitignoreに.envを追加すれば、Gitには流出しません。
# .gitignore
.env
しかし、Claude Codeのようなエージェントはローカルファイルを直接読めるので、.gitignoreは意味がありません。
ユーザー: このプロジェクトの設定ファイルを確認して
Claude Code: .envを読みます...
→ DATABASE_URL, API_KEY が丸見え!
5つの防御層
Claude Codeには、この問題に対する複数の防御メカニズムがあります。
防御層1: パーミッションルール(.envの読み取りを禁止)
防御層2: サンドボックス(OS レベルでアクセス制限)
防御層3: CLAUDE.md(AIへの行動指針)
防御層4: Hooks(ツール実行前に自動チェック)
防御層5: パーミッションモード(操作の承認制御)
外側から順に、多層防御の考え方で守ります。
防御層1:パーミッションルール(最重要)
.envファイルの読み取り自体を禁止します。これが最も確実な防御です。
設定方法
プロジェクトルートに.claude/settings.jsonを作成します。
{
"permissions": {
"deny": [
"Read(.env)",
"Read(.env.*)",
"Read(secrets/**)",
"Read(credentials.json)",
"Read(*.p8)",
"Read(*.p12)",
"Edit(.env)",
"Edit(.env.*)"
]
}
}
効果
この設定により、Claude Codeが.envを読もうとすると自動的にブロックされます。
Claude Code: .envを読みます
→ ブロック!パーミッションルールで拒否されました
ユーザーの許可も求められず、問答無用で拒否されます。
パーミッションの評価順序
deny(拒否) → ask(確認) → allow(許可)
denyが最優先で評価されるため、他のルールでallowされていても、denyに含まれるファイルは読めません。
スコープ別の設定ファイル
| ファイル | スコープ | チーム共有 |
|---|---|---|
~/.claude/settings.json |
全プロジェクト共通 | 不可 |
.claude/settings.json |
プロジェクト固有 | 可(Git管理) |
.claude/settings.local.json |
プロジェクト(個人) | 不可 |
チーム開発では、.claude/settings.jsonをGitで共有すると全員に同じセキュリティルールが適用されます。
防御層2:サンドボックス
OSレベルでClaude Codeのファイルアクセスとネットワーク通信を制限します。
有効化
claude> /sandbox
設定例
{
"sandbox": {
"enabled": true,
"filesystem": {
"denyRead": [
"~/.ssh/private_keys",
".env"
],
"denyWrite": [
"/System",
"/Library"
]
},
"network": {
"allowedDomains": [
"github.com",
"npmjs.com"
]
}
}
}
効果
| 制限 | 説明 |
|---|---|
denyRead |
指定ファイルの読み取りをOSレベルで禁止 |
denyWrite |
指定パスへの書き込みをOSレベルで禁止 |
allowedDomains |
指定ドメイン以外へのネットワーク通信を遮断 |
パーミッションルール(防御層1)はClaude Code内部の制御ですが、サンドボックスはOSレベルの制御です。仮にClaude Codeの内部制御をすり抜けても、OSが止めてくれます。
macOSではSeatbelt、LinuxではBubblewrapが使われます。
防御層3:CLAUDE.md
CLAUDE.mdに秘密情報に関するルールを明記します。
## セキュリティルール
### 絶対にやってはいけないこと
- APIキー・シークレット・パスワードをコードや出力にハードコードしない
- `.env`、`Secrets.plist`、`*.p8`、`*.p12`をgit commitしない
- 認証情報をログやコンソールに出力しない
### ファイル操作
- 機密情報を含むファイル(`.env`、`credentials.json`等)を検知したら警告する
- コード例にAPIキーを含めない(`YOUR_API_KEY`等のプレースホルダーを使う)
パーミッションルールとの違い
| 機能 | パーミッションルール | CLAUDE.md |
|---|---|---|
| 強制力 | 強い(システムレベルで拒否) | 弱い(指示ベース) |
| 柔軟性 | ファイルパスのみ | 自然言語で複雑なルールを記述可能 |
| 用途 | 「このファイルを読むな」 | 「秘密情報を出力するな」「検知したら警告しろ」 |
パーミッションルールは「読ませない」、CLAUDE.mdは「読んでも出力させない・適切に扱わせる」という役割分担です。
防御層4:Hooks(自動チェック)
Hooksは、ツール実行の前後に自動でスクリプトを実行する仕組みです。
例:保護ファイルへの書き込みをブロック
.claude/settings.jsonにHookを設定します。
{
"hooks": {
"PreToolUse": [
{
"matcher": "Edit|Write",
"hooks": [
{
"type": "command",
"command": "./.claude/hooks/protect-files.sh"
}
]
}
]
}
}
.claude/hooks/protect-files.sh:
#!/bin/bash
INPUT=$(cat)
FILE_PATH=$(echo "$INPUT" | jq -r '.tool_input.file_path // empty')
# 保護対象ファイル
PROTECTED=(".env" ".env.local" "credentials.json" "Secrets.plist")
for pattern in "${PROTECTED[@]}"; do
if [[ "$FILE_PATH" == *"$pattern"* ]]; then
echo "ブロック: $FILE_PATH は保護対象のファイルです" >&2
exit 2 # exit 2 = 操作をブロック
fi
done
exit 0 # 許可
例:Bashコマンドの監査ログ
すべてのBashコマンドを記録するHookです。
{
"hooks": {
"PostToolUse": [
{
"matcher": "Bash",
"hooks": [
{
"type": "command",
"command": "jq -r '.tool_input.command' >> ~/.claude/command-audit.log"
}
]
}
]
}
}
何が実行されたかを後から確認できます。
Hookの種類
| Hook | タイミング | 用途 |
|---|---|---|
PreToolUse |
ツール実行前 | 危険な操作のブロック |
PostToolUse |
ツール実行後 | 監査ログの記録 |
ConfigChange |
設定変更時 | 設定変更の追跡 |
防御層5:パーミッションモード
Claude Code全体の動作モードを切り替えます。
| モード | 動作 |
|---|---|
default |
初回のツール使用時に確認(デフォルト) |
acceptEdits |
ファイル編集は自動許可、コマンドは確認 |
plan |
分析のみ。ファイル変更・コマンド実行は禁止 |
dontAsk |
事前許可されたツール以外は自動拒否 |
設定方法
{
"defaultMode": "default"
}
チーム開発で.envを含むプロジェクトを扱う場合、最初はplanモードで分析し、安全を確認してからdefaultに切り替えるという運用も有効です。
実践:推奨設定テンプレート
以下は、一般的なWebプロジェクト向けの推奨設定です。.claude/settings.jsonとして保存し、Gitで共有できます。
{
"permissions": {
"allow": [
"Bash(npm run build)",
"Bash(npm run test)",
"Bash(git status)",
"Bash(git log *)",
"Bash(git diff *)"
],
"ask": [
"Bash(git push *)",
"Bash(git commit *)",
"Bash(npm install *)"
],
"deny": [
"Read(.env)",
"Read(.env.*)",
"Read(secrets/**)",
"Read(credentials.json)",
"Read(*.p8)",
"Read(*.p12)",
"Edit(.env)",
"Edit(.env.*)",
"Bash(curl *)",
"Bash(wget *)"
]
}
}
ポイント
-
deny:
.env系と機密ファイルの読み取りを完全ブロック。curl/wgetも外部送信リスクがあるのでブロック -
ask:
git pushやnpm installは確認を挟む(意図しないpushやパッケージ追加を防止) - allow: ビルド・テスト・ログ確認は自動許可(開発効率を維持)
セキュリティチェックリスト
プロジェクト開始時に確認すべき項目をまとめます。
パーミッションルール
-
.env、.env.*がdenyに含まれているか -
secrets/ディレクトリがdenyに含まれているか -
curl、wgetがdenyに含まれているか -
git pushがaskに設定されているか
CLAUDE.md
- 秘密情報のハードコード禁止ルールがあるか
- 機密ファイル検知時の警告ルールがあるか
- コード例でのプレースホルダー使用ルールがあるか
.gitignore
-
.envが含まれているか -
.claude/settings.local.jsonが含まれているか(個人設定)
確認コマンド
claude> /permissions パーミッション設定の確認
claude> /sandbox サンドボックス状態の確認
claude> /hooks Hook設定の確認
まとめ
| 防御層 | 機能 | 強制力 | 設定場所 |
|---|---|---|---|
| パーミッションルール | ファイル読み取り・コマンド実行の制御 | 強 | .claude/settings.json |
| サンドボックス | OSレベルのアクセス制限 | 最強 | settings.json |
| CLAUDE.md | AIへの行動指針 | 中 | プロジェクトルート |
| Hooks | ツール実行前後の自動チェック | 強 | settings.json |
| パーミッションモード | 全体の動作モード切り替え | 強 | settings.json |
元記事で指摘された「.envがAIから見える」問題に対して、Claude Codeは複数の防御層を用意しています。
最も重要なのは防御層1のパーミッションルールです。.claude/settings.jsonでdenyルールを設定するだけで、.envの読み取りをシステムレベルで禁止できます。
AIエージェントの便利さを享受しながら、秘密情報を守る。そのためのツールはすでにClaude Codeに備わっています。
参考
- https://docs.anthropic.com/en/docs/claude-code/security
- https://docs.anthropic.com/en/docs/claude-code/settings
- https://docs.anthropic.com/en/docs/claude-code/hooks
@kotaro_ai_lab
AI活用や開発効率化について発信しています。フォローお気軽にどうぞ!