「
--dangerously-skip-permissionsを使わないと遅くてやってられない...」そう思っていませんか? 実は、適切なPermission設定によって、セキュリティを維持しながらYOLO並みのスピードを実現できます。
この記事では、Claude Codeの権限管理を体系的に解説し、実践的な設定例を提供します。
目次
- Permission設定の重要性
- 設定ファイルの階層構造
- Permissionsの評価ロジック
- Sandbox Modeで自動許可を実現
- deny設定すべきコマンド一覧
- 3層防御モデル
- 実践的な設定例
- トラブルシューティング
- まとめ
Permission設定の重要性
Claude Codeの基本原則
Claude Codeは「起動しただけでシステムに変更を加えない」という設計思想で作られています。ファイルの編集、テストの実行、Bashコマンドの実行など、副作用を伴うすべての操作には明示的な許可が必要です。
これは素晴らしいセキュリティ原則ですが、実際に使ってみると...
🔐 Claude wants to run: npm run test
❯ Yes
No
Always allow for this project
🔐 Claude wants to run: git status
❯ Yes
No
Always allow for this project
🔐 Claude wants to run: npm run lint
❯ Yes
No
Always allow for this project
... (以下100回続く)
ひたすら矢印キーでYesを選んでEnterを押し続ける作業に疲弊していませんか?
3つの選択肢
許可プロンプトが表示されたとき、以下の選択肢があります
| 選択 | 効果 |
|---|---|
| Yes | 今回だけ許可 |
| No | 今回は拒否 |
| Always allow for this project | 今後このプロジェクトで常に許可 |
「Always allow」を選べばいいじゃん、と思うかもしれません。しかし、これには問題があります
- その場その場で判断が必要 - 事前に安全なコマンドを定義できない
- チーム共有が困難 - 個人の許可設定は他のメンバーに伝わらない
- 危険なコマンドも「Always allow」で許可される可能性 - うっかりミスのリスク
解決策: 宣言的なPermission設定
Permission設定をsettings.jsonに明示的に記述することで
- 事前に安全なコマンドを定義できる
- 危険なコマンドを確実にブロックできる
- チームで設定を共有できる
- 許可プロンプトの数を大幅に削減できる
設定ファイルの階層構造
3層スコープ
Claude Codeの設定は、3つのレベルで管理されます
┌─────────────────────────────────────────┐
│ Managed (最優先) │
│ IT部門による強制設定。ユーザーは上書き不可 │
└─────────────────────────────────────────┘
↓ オーバーライドされる
┌─────────────────────────────────────────┐
│ Project │
│ プロジェクト単位の設定。Git管理で共有 │
└─────────────────────────────────────────┘
↓ オーバーライドされる
┌─────────────────────────────────────────┐
│ User │
│ 個人設定。全プロジェクトに適用 │
└─────────────────────────────────────────┘
設定ファイルのパス
| スコープ | パス | 用途 |
|---|---|---|
| Managed |
/etc/claude-code/managed-settings.json (Linux) /Library/Application Support/ClaudeCode/managed-settings.json (macOS) |
エンタープライズ向け強制設定 |
| Project (共有) | .claude/settings.json |
チーム共有設定(Gitにコミット) |
| Project (ローカル) | .claude/settings.local.json |
個人のプロジェクト設定(gitignore) |
| User | ~/.claude/settings.json |
個人の全体設定 |
settings.local.jsonの活用
.claude/settings.local.jsonは.gitignoreに追加して、個人の設定を保存するのに最適です
// .claude/settings.local.json
// 機密情報や個人的な設定はここに
{
"permissions": {
"allow": [
"Bash(my-personal-script *)"
]
}
}
これにより、チーム共有の設定を汚さずに個人の設定を追加できます。
Permissionsの評価ロジック
評価順序: deny → ask → allow
Permission設定は以下の順序で評価されます
deny → マッチしたら即拒否(最優先)
↓
ask → マッチしたら確認プロンプト
↓
allow → マッチしたら自動許可
↓
default → どれにもマッチしなければ確認プロンプト
denyが最優先というのが重要です。これにより、「基本的に許可するが、特定の操作だけは絶対に禁止」という設定が可能になります。
パターンマッチングの記法
Bashコマンド
| パターン | マッチするコマンド |
|---|---|
Bash |
すべてのBashコマンド |
Bash(npm run build) |
完全一致のみ |
Bash(npm run *) |
npm runで始まるすべて |
Bash(* --help) |
--helpで終わるすべて |
Bash(git * main) |
git checkout main、git merge main等 |
重要: スペースの有無で挙動が変わる
// Bash(ls *) - "ls -la"にマッチ、"lsof"にはマッチしない
// Bash(ls*) - "ls -la"にも"lsof"にもマッチ
スペース+*は単語境界を強制します。これを知らないと意図しない許可が発生する可能性があります。
ファイル読み書き
gitignore形式のパターンを使用します
| パターン | 意味 |
|---|---|
Read(./.env) |
カレントディレクトリの.envのみ |
Read(./.env*) |
.envで始まるすべてのファイル |
Read(~/.ssh/**) |
ホームの.ssh配下すべて |
Edit(/src/**/*.ts) |
設定ファイルからの相対パス |
Read(//etc/passwd) |
絶対パス(//で始める) |
注意: /pathは設定ファイルからの相対パス、//pathが絶対パスです。
WebFetch
"allow": [
"WebFetch(domain:github.com)",
"WebFetch(domain:npmjs.com)"
]
ドメイン指定でアクセス可能なURLを制限できます。
MCP
"allow": [
"mcp__github__*", // github MCPサーバーのすべてのツール
"mcp__puppeteer__puppeteer_navigate" // 特定のツールのみ
]
Sandbox Modeで自動許可を実現
Sandboxとは
Sandboxは、ファイルシステムとネットワークをOS レベルで制限する機能です。「この範囲内なら安全に自動実行してOK」という境界を定義します。
┌──────────────────────────────────────────────┐
│ Sandbox境界 │
│ ┌────────────────────────────────────────┐ │
│ │ ワーキングディレクトリ配下 │ │
│ │ - ファイル読み書きOK │ │
│ │ - サブプロセス実行OK │ │
│ └────────────────────────────────────────┘ │
│ │
│ ✗ 親ディレクトリへの書き込み │
│ ✗ 許可されていないドメインへの通信 │
└──────────────────────────────────────────────┘
有効化方法
# インタラクティブに設定
> /sandbox
またはsettings.jsonに記述:
{
"sandbox": {
"enabled": true,
"autoAllowBashIfSandboxed": true
}
}
OS別の実装
| OS | 実装技術 | 備考 |
|---|---|---|
| macOS | Seatbelt (sandbox-exec) | 追加インストール不要 |
| Linux | bubblewrap (bwrap) | apt install bubblewrap socat |
| WSL2 | bubblewrap | Linux同様 |
| WSL1 | 非対応 | カーネル機能の制限により |
git worktree併用時の問題と解決策
git worktreeなど、ワーキングディレクトリ外のファイルにアクセスする必要があるコマンドは、Sandboxと競合します。
問題: git worktreeは親リポジトリの.gitディレクトリを参照するため、sandbox制限に引っかかります。
Exit code 128
fatal: Unable to create '/Users/.../repo/.git/worktrees/feature/index.lock': Operation not permitted
解決策: excludedCommandsでSandbox対象外に指定します。
{
"sandbox": {
"enabled": true,
"autoAllowBashIfSandboxed": true,
"excludedCommands": ["git", "docker"]
}
}
excludedCommandsの記法について
公式ドキュメントでは ["git", "docker"] のようにコマンド名をそのまま指定します。
ただし、一部の環境やバージョンで動作しない場合があるという報告があります。その場合は、旧構文である:*サフィックスを試してください:
{
"sandbox": {
"excludedCommands": ["git:*", "docker:*"]
}
}
Note: Bash permissionルールにおける
:*サフィックス(例:Bash(npm run:*))は現在非推奨で、*(スペース+アスタリスク)と同等です。excludedCommandsでの:*がどのバージョンで必要かは環境依存のため、動作しない場合は両方試してみてください。
git worktree併用時の推奨設定
{
"sandbox": {
"enabled": true,
"autoAllowBashIfSandboxed": true,
"excludedCommands": ["git"],
"network": {
"allowUnixSockets": ["~/.ssh/agent-socket"]
}
}
}
deny設定すべきコマンド一覧
カテゴリ別リスト
1. 使用しないツール(トークン節約効果あり)
"deny": [
"NotebookEdit", // Jupyter未使用なら
"NotebookRead"
]
Jupyter Notebookを使わないプロジェクトでは、これらをdenyすることでClaudeがこれらのツールを考慮しなくなり、レスポンスが若干早くなります。
2. 危険なgit操作
"deny": [
"Bash(git add .)", // 意図しないファイルのステージング防止
"Bash(git add -A)", // 同上
"Bash(git push -f *)", // 強制プッシュ防止
"Bash(git push --force *)",
"Bash(git reset --hard *)", // 変更の破棄防止
"Bash(git checkout .)", // 変更の破棄防止
"Bash(git clean -f *)", // 追跡外ファイルの削除防止
"Bash(git -C *)" // 他ディレクトリでの操作防止
]
3. デプロイ・公開系
"deny": [
"Bash(npm publish *)", // 意図しないパッケージ公開防止
"Bash(yarn publish *)",
"Bash(pnpm publish *)",
"Bash(*deploy*)", // デプロイコマンド全般
"Bash(terraform apply *)" // インフラ変更
]
4. 機密ファイル保護
"deny": [
"Read(./.env)",
"Read(./.env.*)",
"Read(./.dev.vars)",
"Edit(./.env)",
"Edit(./.env.*)",
"Read(~/.ssh/**)", // SSH鍵
"Read(~/.aws/**)", // AWS認証情報
"Read(~/.config/gcloud/**)" // GCP認証情報
]
5. ビルトインツールで代替可能なもの
"deny": [
"Bash(grep *)", // → Grepツールを使用
"Bash(find *)", // → Globツールを使用
"Bash(cat *)", // → Readツールを使用
"Bash(head *)", // → Readツールを使用
"Bash(tail *)", // → Readツールを使用
"Bash(curl *)", // → WebFetchツールを使用
"Bash(wget *)" // → WebFetchツールを使用
]
これらのコマンドはClaude Codeの専用ツールで代替可能で、そちらの方が安全かつ効率的です。
重要: denyの信頼性について
denyは「AIへの指示」であり、絶対的な制限ではありません。
deny設定は、Claudeに「このコマンドは使わないでね」と伝えているに過ぎません。悪意のあるプロンプトインジェクション等により、Claudeが設定を無視する可能性はゼロではありません。
本当に絶対的な制御が必要な場合は、後述のHooksを使用してください。
3層防御モデル
Claude Codeのセキュリティは、3つの層で構成されています:
┌─────────────────────────────────────────────────────────┐
│ Layer 1: Sandbox │
│ │
│ OS レベルでのファイル/ネットワーク制限 │
│ → 突破されにくいが、一部コマンドは除外が必要 │
└─────────────────────────────────────────────────────────┘
↓
┌─────────────────────────────────────────────────────────┐
│ Layer 2: Permissions │
│ │
│ allow/deny/askルールによる制御 │
│ → AIへの指示。柔軟だが確実性はやや低い │
└─────────────────────────────────────────────────────────┘
↓
┌─────────────────────────────────────────────────────────┐
│ Layer 3: Hooks │
│ │
│ シェルスクリプトによる決定論的制御 │
│ → 完全に確実。ただし設定が複雑 │
└─────────────────────────────────────────────────────────┘
各層の特徴
| 層 | 確実性 | 柔軟性 | 設定難易度 |
|---|---|---|---|
| Sandbox | 高 | 低 | 易 |
| Permissions | 中 | 高 | 易 |
| Hooks | 最高 | 最高 | 難 |
Hooksによる決定論的制御
Hooksは、ツール実行前後にシェルスクリプトを実行する機能です
{
"hooks": {
"PreToolUse": [
{
"matcher": "Bash",
"command": "/path/to/validate-command.sh"
}
]
}
}
Hookスクリプトが非ゼロで終了すると、ツール実行がブロックされます。これはAIの判断を介さない、完全に決定論的な制御です。
例: 危険なgitコマンドを確実にブロックするHook
#!/bin/bash
# validate-git.sh
# 入力からコマンドを取得
COMMAND="$CLAUDE_TOOL_INPUT"
# 危険なパターンをチェック
if echo "$COMMAND" | grep -qE "git (push -f|reset --hard|clean -f)"; then
echo "BLOCK: Dangerous git command detected"
exit 1
fi
exit 0
実践的な設定例
個人開発者向け(バランス型)
セキュリティと生産性のバランスを取った設定
{
"permissions": {
"allow": [
"Bash(npm run *)",
"Bash(yarn *)",
"Bash(pnpm *)",
"Bash(git status)",
"Bash(git diff *)",
"Bash(git log *)",
"Bash(git branch *)",
"Bash(git checkout *)",
"Bash(git commit *)",
"Bash(git pull *)",
"Bash(git fetch *)",
"Bash(ls *)",
"Bash(which *)",
"Bash(echo *)",
"Bash(pwd)",
"Bash(date *)",
"Read(~/.zshrc)",
"Read(~/.bashrc)"
],
"ask": [
"Bash(git push *)",
"Bash(rm *)",
"Bash(mv *)"
],
"deny": [
"Bash(git add .)",
"Bash(git add -A)",
"Bash(git push -f *)",
"Bash(git reset --hard *)",
"Bash(npm publish *)",
"Bash(curl *)",
"Bash(wget *)",
"Read(./.env)",
"Read(./.env.*)",
"Edit(./.env)",
"Edit(./.env.*)",
"Read(~/.ssh/**)",
"Read(~/.aws/**)"
]
},
"sandbox": {
"enabled": true,
"autoAllowBashIfSandboxed": true,
"excludedCommands": ["git", "docker"]
}
}
チーム開発向け(厳格型)
チームで共有する.claude/settings.json用
{
"permissions": {
"allow": [
"Bash(npm run test *)",
"Bash(npm run lint *)",
"Bash(npm run build *)",
"Bash(npm run dev *)",
"Bash(git status)",
"Bash(git diff *)",
"Bash(git log *)"
],
"ask": [
"Bash(npm install *)",
"Bash(git *)"
],
"deny": [
"Bash(git add .)",
"Bash(git add -A)",
"Bash(git push *)",
"Bash(git push -f *)",
"Bash(git reset *)",
"Bash(npm publish *)",
"Bash(rm -rf *)",
"Bash(curl *)",
"Bash(wget *)",
"Bash(*deploy*)",
"Bash(terraform *)",
"Read(./.env)",
"Read(./.env.*)",
"Edit(./.env)",
"Edit(./.env.*)",
"Read(./secrets/**)",
"Edit(./secrets/**)"
]
},
"sandbox": {
"enabled": true,
"autoAllowBashIfSandboxed": true,
"excludedCommands": ["git"]
}
}
エンタープライズ向け(Managed設定)
IT部門が配布するmanaged-settings.json
{
"permissions": {
"deny": [
"Bash(curl *)",
"Bash(wget *)",
"Bash(nc *)",
"Bash(netcat *)",
"Bash(ssh *)",
"Bash(scp *)",
"Bash(rsync *)",
"Bash(*deploy*)",
"Bash(terraform *)",
"Bash(aws *)",
"Bash(gcloud *)",
"Bash(az *)",
"Read(~/.ssh/**)",
"Read(~/.aws/**)",
"Read(~/.config/gcloud/**)",
"Read(~/.azure/**)",
"WebFetch"
],
"disableBypassPermissionsMode": "disable"
},
"sandbox": {
"enabled": true,
"autoAllowBashIfSandboxed": false,
"allowUnsandboxedCommands": false
}
}
この設定は
- ネットワーク系コマンドを全面禁止
- クラウド認証情報へのアクセスを禁止
- WebFetchを禁止(社内ネットワークからの情報漏洩防止)
-
--dangerously-skip-permissionsを無効化 - Sandboxの自動許可を無効化
トラブルシューティング
「許可したはずなのに聞かれる」
原因1: パターンが厳密すぎる
// 問題: npm run testだけ許可
"allow": ["Bash(npm run test)"]
// 実際のコマンド: npm run test -- --coverage
// → マッチしない!
// 解決: ワイルドカードを使う
"allow": ["Bash(npm run test *)"]
原因2: deny/askが優先されている
evaluateの順序を思い出してください: deny → ask → allow
// 問題
"ask": ["Bash(git *)"], // ← こちらが先にマッチ
"allow": ["Bash(git status)"] // ← 到達しない
// 解決: askをより限定的に
"ask": ["Bash(git push *)"],
"allow": ["Bash(git status)"]
「Sandboxでgitが動かない」
原因: git worktreeとSandboxの競合、またはexcludedCommandsの設定ミス
// 公式ドキュメントの記法
"excludedCommands": ["git"]
// 動作しない場合は旧構文を試す
"excludedCommands": ["git:*"]
git worktree使用時は特にこの問題に遭遇しやすいです。
「denyしたのに実行された」
原因: AIが別の方法を見つけた
denyはAIへの指示であり、絶対ではありません。例えば:
"deny": ["Bash(rm *)"]
としても、Claudeはfind . -deleteで同じ結果を達成しようとするかもしれません。
解決: Hooksを使う
決定論的な制御が必要な場合は、PreToolUse Hookを使用してください。
設定の検証方法
# 現在の設定を確認
> /permissions
# 設定ファイルの読み込み順を確認
# (Managed → Project → User の順でマージされる)
まとめ
ベストプラクティス7箇条
- Sandboxを有効化する - 最初の防御層として必須
- excludedCommandsは慎重に設定する - Sandbox外で実行するコマンドは最小限に
- denyを最初に設定する - 「やってはいけないこと」を明確に
- allowは必要最小限に - 過剰な許可はセキュリティリスク
-
機密ファイルは必ずdeny -
.env、認証情報ファイル等 -
チーム設定は
.claude/settings.jsonに - Gitで共有 -
個人設定は
.claude/settings.local.jsonに - gitignoreに追加
