結論:Claude Codeの権限は「4段階」ではなく「組み合わせ」で考える
Claude CodeにWrite権限を渡した瞬間、想定していなかったファイルが書き換えられました──権限モデルの全パターンを体系的に検証した結果を共有します。
この記事でわかることは3つです。
- Claude Codeの4つの権限(Read / Write / Edit / Bash)それぞれが実際に何を許可するのか
- 権限の組み合わせで起きる想定外の挙動と、修正後の現在の振る舞い
- プロジェクト種別ごとの推奨設定と、hooks機能を使った安全策
環境・前提条件
| 項目 | 値 |
|---|---|
| Claude Code | 最新版(2025年7月時点) |
| OS | macOS Sequoia 15.x / Ubuntu 24.04 |
| シェル | zsh / bash |
| 検証方法 | 各権限パターンで同一プロンプトを実行し、操作ログとファイル差分を記録 |
注意: Claude Codeは頻繁にアップデートされます。本記事の検証結果は2025年7月時点のものです。最新の挙動は公式ドキュメントで確認してください。
1. Claude Codeの権限モデル4種──公式仕様の整理
Claude Codeは、AIエージェントがローカル環境で実行できる操作をツール単位の権限で制御しています。大きく分けて以下の4カテゴリです。
権限の一覧と概要
| 権限カテゴリ | 対応する主なツール | 概要 |
|---|---|---|
| Read |
View, GlobTool, GrepTool, LS
|
ファイル・ディレクトリの読み取り |
| Edit | Edit |
既存ファイルの部分的な編集(差分パッチ適用) |
| Write | Write |
新規ファイル作成・既存ファイルの全体上書き |
| Bash | Bash |
任意のシェルコマンド実行 |
重要なポイントは、Claude Codeのパーミッションプロンプトの仕組みです。デフォルトではRead以外のすべての操作で承認を求めるプロンプトが表示されます。--allowedTools フラグや .claude/settings.json の設定により、特定のツールを事前承認(自動許可)できます。
パーミッションプロンプトの仕組み
Claude Codeを起動すると、Read系ツールは自動で許可されますが、それ以外は毎回ユーザー承認が必要です。承認時の選択肢は以下の通りです。
- Yes – 今回だけ許可
- Yes, allow for this session – このセッション中は同じツールを自動許可
- Yes, always allow – 永続的に自動許可(設定ファイルに記録)
2. 各権限の組み合わせパターン検証──実際に何が起きるか
同一のプロンプト「このプロジェクトにESLintの設定を追加して」を、権限の事前承認パターンを変えて実行しました。
検証マトリクス
| パターン | Read | Edit | Write | Bash | 結果 |
|---|---|---|---|---|---|
| ① 最小 | ✅ | ❌ | ❌ | ❌ | 読み取りのみ。設定案をテキストで提示するだけ |
| ② Read+Edit | ✅ | ✅ | ❌ | ❌ | 既存の package.json にeslint依存を追記。新規ファイル(.eslintrc.js)は作れず、承認プロンプトが出る |
| ③ Read+Write | ✅ | ❌ | ✅ | ❌ |
.eslintrc.js を新規作成。既存ファイルの編集時もWriteツール(全体上書き)で対応 |
| ④ Read+Edit+Write | ✅ | ✅ | ✅ | ❌ | 既存ファイルはEditで差分適用、新規ファイルはWriteで作成。最も意図通りの動作 |
| ⑤ 全許可 | ✅ | ✅ | ✅ | ✅ | 上記に加え npm install eslint を自動実行。node_modules 配下に大量のファイルが生成
|
| ⑥ Read+Bash | ✅ | ❌ | ❌ | ✅ | Bashのechoやリダイレクトでファイル作成を試みる。Write権限がなくてもファイル書き込みが可能
|
パターン⑥の重要な発見
Bash権限はWrite/Edit権限を実質的にバイパスします。 echo "content" > file.txt のようなシェルコマンドでファイルを作成・上書きできるため、Bash権限を許可する場合はWrite/Editの制限が事実上無意味になります。
これは権限モデルとして意図された動作です。Bash権限は「任意のシェルコマンド実行」であり、その中にはファイル操作も含まれるためです。Bash権限は最上位権限として認識すべきです。
3. Write権限に関する過去の挙動と現在の状態
過去に報告されていた問題
Claude Codeの初期バージョンでは、Write権限の制御に関していくつかの問題が報告されていました。
- ワーキングディレクトリ外のファイルへの書き込み: プロジェクトルート外のファイルに対してもWriteが実行される場合がある
- 承認プロンプトのスキップ: 特定の条件下でパーミッションプロンプトが表示されないケースがある
現在の挙動(2025年7月時点での再検証)
現在のバージョンでは、以下の安全機構が実装されています。
- プロジェクトルート制限: デフォルトではカレントディレクトリとそのサブディレクトリのみが操作対象
-
パス正規化:
../を使った親ディレクトリへの書き込み試行は、承認プロンプトが表示される -
設定ファイルによるパス制御:
.claude/settings.jsonでさらに細かい制御が可能
{
"permissions": {
"allow": [
"Edit",
"Write"
],
"deny": [
"Bash"
]
}
}
ただし、allowedToolsでBashを包括的に許可している場合、上記の制限はBash経由で迂回可能です。これはバグではなく仕様ですが、認識しておくべき重要なポイントです。
4. プロジェクト種別ごとの推奨権限設定マトリクス
すべてのプロジェクトに同じ権限設定を適用するのは危険です。以下は、プロジェクトの性質に応じた推奨設定です。
具体的な設定例
個人プロジェクト・プロトタイプ
{
"permissions": {
"allow": [
"Edit",
"Write",
"Bash(npm run *)",
"Bash(npx *)",
"Bash(git diff *)",
"Bash(git log *)"
]
}
}
Bashは特定のコマンドパターンのみ許可。npm run や git の読み取り系コマンドに限定します。
チーム開発・社内プロジェクト
{
"permissions": {
"allow": [
"Edit",
"Write"
],
"deny": [
"Bash"
]
}
}
Bashは原則禁止。コード変更はEdit/Writeのみに限定し、ビルドやテストは手動で実行します。
本番コード・機密リポジトリ
{
"permissions": {
"allow": [],
"deny": [
"Edit",
"Write",
"Bash"
]
}
}
Read onlyで運用。コードレビューやアーキテクチャ相談のみに使用します。提案されたコードは手動で適用します。
5. hooks機能と組み合わせた権限制御の安全策
Claude Codeのhooks機能を使うと、ツール実行の前後にカスタムスクリプトを挟めます。これにより、権限設定だけでは実現できない細かい制御が可能になります。
hooksの基本構造
.claude/settings.json に以下のように定義します。
{
"hooks": {
"PreToolUse": [
{
"matcher": "Write",
"hooks": [
{
"type": "command",
"command": "python3 .claude/hooks/validate_write.py \"$CLAUDE_FILE_PATH\""
}
]
}
],
"PostToolUse": [
{
"matcher": "Bash",
"hooks": [
{
"type": "command",
"command": "bash .claude/hooks/audit_bash.sh"
}
]
}
]
}
}
実用的なhooksパターン
パターン1: 特定ディレクトリへの書き込みをブロック
#!/usr/bin/env python3
# .claude/hooks/validate_write.py
import sys
import json
import os
def main():
input_data = json.loads(sys.stdin.read())
tool_input = input_data.get("tool_input", {})
file_path = tool_input.get("file_path", "")
blocked_dirs = ["config/", "secrets/", ".env", "migrations/"]
for blocked in blocked_dirs:
if file_path.startswith(blocked) or blocked in file_path:
result = {
"decision": "block",
"reason": f"書き込みがブロックされました: {file_path} は保護対象ディレクトリに含まれます"
}
print(json.dumps(result))
sys.exit(0)
result = {"decision": "approve"}
print(json.dumps(result))
if __name__ == "__main__":
main()
パターン2: Bashコマンドの監査ログ
#!/bin/bash
# .claude/hooks/audit_bash.sh
TIMESTAMP=$(date -u +"%Y-%m-%dT%H:%M:%SZ")
echo "${TIMESTAMP} | Bash executed | $(cat /dev/stdin | jq -r '.tool_input.command')" \
>> .claude/audit.log
パターン3: 危険なBashコマンドのブロック
#!/usr/bin/env python3
# .claude/hooks/validate_bash.py
import sys
import json
import re
def main():
input_data = json.loads(sys.stdin.read())
command = input_data.get("tool_input", {}).get("command", "")
dangerous_patterns = [
r"rm\s+-rf\s+/",
r"chmod\s+777",
r"curl.*\|\s*bash",
r"wget.*\|\s*sh",
r">\s*/etc/",
r"sudo\s+",
]
for pattern in dangerous_patterns:
if re.search(pattern, command):
result = {
"decision": "block",
"reason": f"危険なコマンドがブロックされました: {command}"
}
print(json.dumps(result))
sys.exit(0)
result = {"decision": "approve"}
print(json.dumps(result))
if __name__ == "__main__":
main()
hooks活用のポイント
-
PreToolUse フックで
blockを返すと、ツール実行自体がキャンセルされる - PostToolUse フックは監査ログや通知に適している
- hookスクリプト自体がセキュリティホールにならないよう、hookファイルの権限管理も忘れずに
まとめ:最小権限の原則をClaude Codeにどう適用するか
- Bash権限はWrite/Editを包含する最上位権限です。Bashを許可する場合、Write/Editの制限は実質的に無効化されることを認識した上で、Bashは特定コマンドパターンのみを許可するのが鉄則です
-
プロジェクトのリスクレベルに応じて権限を段階的に設定しましょう。個人プロジェクトでは
Edit + Write + 限定Bash、本番コードではRead onlyが推奨です。一律の設定は事故のもとです - hooks機能は権限モデルの「穴」を埋める最後の砦です。ディレクトリ単位のブロック、危険コマンドのフィルタ、監査ログの3点セットを導入することで、万が一の事故を防止・検出できます