Claude Codeのpermissions.allow・denyの違い|安全な設定例

Claude Codeのpermissions.allow・denyの違い|安全な設定例 AI開発

Claude Codeのsettings.jsonには、permissions.allowとpermissions.denyがあります。名前から「allowに書いたものだけが使える」「denyに書いたものは絶対に実行されない」と考えがちですが、どちらも少し違います。この思い込みのまま設定すると、書いたつもりのルールが効かなかったり、逆に守れていないファイルが出たりします。

この記事では、allowとdeny(とask)が実際に何を決めるのか、評価の順番、書き方の落とし穴、そして安全に使える設定例を、Claude Codeの公式ドキュメント(2026年10月時点)に沿って整理します。チーム運用やCI向けの設定はClaude Code 権限・パーミッション設定完全ガイド|settings.jsonでツール使用を制御してチーム運用を安全にする方法、プロンプトインジェクションや機密情報の全体像はClaude Code セキュリティ完全ガイド|プロンプトインジェクション対策・権限設計・機密情報保護・監査ログで扱っているので、ここでは「ルールの意味と落とし穴」に絞ります。

スポンサーリンク

allow・ask・denyの役割

permissionsには3種類のルールを書けます。

  • allow:確認ダイアログなしで実行を許可する
  • ask:実行のたびに確認を求める
  • deny:実行を拒否する

ここで大事なのは、allowは「このツールだけを使える」という制限リストではなく、「確認を省く」ためのルールだという点です。書いていない操作が自動で禁止されるわけではなく、通常のモードでは確認ダイアログが出るだけです。禁止したいものはdenyに書きます。

基本の形(.claude/settings.json)
{
  "permissions": {
    "allow": [
      "Bash(npm run test *)",
      "Bash(git diff *)"
    ],
    "ask": [
      "Bash(git push *)"
    ],
    "deny": [
      "Read(./.env)"
    ]
  }
}
キー名はallowです
allowedToolsというキーはありません。--allowedToolsはコマンドラインのフラグ名で、settings.jsonではpermissions.allowを使います。

評価順序はdeny → ask → allow

ルールはdeny、ask、allowの順に評価され、最初に一致したものが結果になります。ルールの具体性は順序に影響しません。

そのため、広いdenyルールが、同じ呼び出しに一致するより具体的なallowルールを上書きします。たとえばdenyにBash(aws *)があると、allowにBash(aws s3 ls)を書いてもブロックされます。「広く禁止して、一部だけ例外で許可する」という書き方はできません。

例外を作れない書き方(NG)
{
  "permissions": {
    "allow": ["Bash(aws s3 ls)"],
    "deny": ["Bash(aws *)"]
  }
}

同じ関係はaskとallowの間にもあります。一致するaskがあれば、より具体的なallowがあっても確認が出ます。例外を作りたいときは、denyやaskの範囲そのものを狭く書きます。

また、複数の設定ファイルをまたぐ場合も同じです。どこかのレベルでdenyされたツールは、他のレベルのallowで許可できません。設定ファイル同士の優先順位はClaude Codeのsettings.jsonが反映されない原因|User・Project・Localの優先順位を参照してください。

allowに書かなかった操作はどうなるか

allowに書いていない操作は、通常の(Default)モードでは確認ダイアログが出ます。拒否されるわけではありません。確認なしで拒否したい場合は、次の2つの方法があります。

  • denyに書く(ルールに一致するものだけを拒否する)
  • dontAskモードにする(確認が必要になる呼び出しを自動で拒否し、allowと事前に承認されたツールだけを実行する)

dontAskモードは、CIなど人が確認できない場面で「許可リスト方式」にしたいときに向いています。その場合、使う予定のツールをallowに漏れなく書いておく必要があります。

確認ダイアログの「常に許可」はどこに保存されるか

確認ダイアログで「Yes, and don’t ask again」を選ぶと、Bashコマンドやドメイン単位のWebFetchは、リポジトリのルートの.claude/settings.local.jsonにallowルールとして保存され、次回以降のセッションにも効きます。ファイル編集の承認だけは、セッションの終了までです。知らないうちにallowが増えるので、定期的に/permissionsで見直すと安全です。

ルールの書き方の基本

ルールはツール名またはツール名(条件)の形です。ツール名だけを書くと、そのツールの全呼び出しに一致します。

  • Bash(npm run test *):シェルコマンド
  • Read(./.env):ファイルの読み取り
  • Edit(src/**):ファイルの編集(Writeなどの編集系ツールを含む)
  • WebFetch(domain:example.com):ドメインを指定したWebFetch
  • mcp__github__get_issue:MCPサーバーのツール
WebFetchはdomain:が必要です
WebFetch(example.com)ではなくWebFetch(domain:example.com)と書きます。

Bashのワイルドカードの落とし穴

Bashのルールはコマンド全体の文字列に対して照合され、*は空白を含む任意の文字列にマッチします。書き方で一致する範囲が大きく変わります。

  • Bash(npm run *)はnpm run buildやnpm run test --watchに一致し、npm installには一致しません。
  • Bash(ls *)はls -laに一致し、lsofには一致しません。Bash(ls*)と空白なしで書くと、lsofにも一致します。末尾の*の前の空白は、ルールの一部です。
  • Bash(git * main)のように途中の*は、サブコマンドもオプションも含めて何にでもマッチします。git -c core.fsmonitor=...のような、プログラムを実行させるオプションも含まれるので、広く許可する場合ほど注意が必要です。

また、timeout、time、nice、nohupなどのラッパーは取り除いてから照合されるため、Bash(npm test *)はtimeout 30 npm testにも一致します。一方、npxやdocker execなどは取り除かれないので、Bash(docker exec *)のような許可は、中で何を実行しても通ってしまいます。内側のコマンドまで含めて書くのが安全です。

読み取り専用コマンドはallowに書かなくていい

ls、cat、echo、pwd、head、tail、grep、find、wc、gitの読み取り専用の形など、組み込みの読み取り専用コマンドは、どのモードでも確認なしで実行されます。そのためBash(ls *)やBash(cat *)をallowに書く必要はありません。このセットは変更できず、確認を求めたい場合はaskかdenyのルールを書きます。

複合コマンドは部分ごとに照合される

&&や;でつないだコマンドは、分解して部分ごとに照合されます。git status && npm testのうちgit statusだけを許可していても、npm testの部分に確認が出ます。確認ダイアログで「常に許可」を選んだ場合も、確認が必要だった部分ごとにルールが保存されます(1回の複合コマンドで最大5つ)。

機密ファイルを守る:Read deny

.envのような機密ファイルは、Readのdenyで守ります。Readのdenyはファイルの読み取りツールに効きますが、.claudeignoreというファイルは効きません。置いている場合は、中身をReadのdenyに移します。

機密ファイルを守る設定
{
  "permissions": {
    "deny": [
      "Read(./.env)",
      "Read(./.env.*)",
      "Read(./secrets/**)"
    ]
  }
}

編集を防ぎたいパスには、Write(パス)ではなくEdit(パス)を使います。Editのルールは、ファイルを編集する組み込みツール全般に適用されます。

パスの書き方:先頭の/は絶対パスではない

パスの書き方には、次の違いがあります。

  • //path:ファイルシステムのルートからの絶対パス
  • ~/path:ホームディレクトリからのパス
  • /path:その設定ファイルの場所を基準にしたパス
  • pathまたは./path:カレントディレクトリからのパス

特に/pathは、プロジェクトの設定では作業ディレクトリ基準ですが、ユーザー設定(~/.claude/settings.json)では~/.claude/基準になります。ユーザー設定にRead(/secrets/**)と書いても、意図した場所は守られません。全プロジェクトに効かせたいときは、~/か//で始めます。

~/.claude/settings.json(全プロジェクト共通)
{
  "permissions": {
    "deny": [
      "Read(~/.ssh/**)",
      "Read(~/.aws/**)"
    ]
  }
}

denyでは防げない迂回

Bashのdenyルールは、Claudeが書いたコマンドの文字列に一致するかを見ているだけで、そのプログラム自体を禁止する仕組みではありません。たとえば次のような別の呼び出し方は、止められません。

  • Bash(curl *)のdeny → /usr/bin/curl ...やsh -c 'curl ...'
  • Bash(rm *)のdeny → /bin/rm ...やbash -c 'rm ...'
  • Bash(git push *)のdeny → git -C . push ...

このため、denyは「Claudeが普通に書くコマンドを止めるガードレール」と考え、本当に破られたくない境界は別の仕組みで守ります。ファイルシステムやネットワークをコマンドの文字列に頼らず制限するにはSandboxを、実行前に独自のロジックでコマンド全体を検査するにはPreToolUse Hookを使います(HookはClaude Code Hooks完全ガイド|Command・HTTP・Prompt・Agentフックの設定と実践活用を参照)。

ツール全体を消すdenyと、範囲を絞るdeny

denyには2つの挙動があります。Bashのようにツール名だけを書くと、そのツールはClaudeのコンテキストから消え、Claudeには存在すら見えなくなります。Bash(rm *)のように範囲を絞ると、ツールは使えるままで、一致する呼び出しだけがブロックされます。

MCPツールの許可と禁止

MCPのツールはmcp__サーバー名__ツール名で指定します。denyではmcp__*のように全MCPツールを対象にできますが、allowのワイルドカードはmcp__github__*のように、サーバー名まで書いた後ろにだけ使えます。"*"や"mcp__*"のallowは、警告が出て無視されます。

安全な設定例

ここまでを踏まえた、プロジェクト向けの設定の一例です。コマンド名やパスは、自分のプロジェクトに合わせて変更してください。

.claude/settings.json(チーム共有)
{
  "$schema": "https://json.schemastore.org/claude-code-settings.json",
  "permissions": {
    "allow": [
      "Bash(npm run test *)",
      "Bash(npm run lint *)",
      "Bash(git diff *)",
      "Bash(git log *)",
      "Bash(git add *)",
      "Bash(git commit *)"
    ],
    "ask": [
      "Bash(git push *)",
      "Bash(npm install *)"
    ],
    "deny": [
      "Read(./.env)",
      "Read(./.env.*)",
      "Read(./secrets/**)",
      "Edit(./package-lock.json)",
      "Bash(rm -rf *)"
    ]
  }
}

この設定のねらいは次のとおりです。

  • テストやLintなど、頻繁に使う安全な操作はallowで確認を省く
  • push・依存の追加など、影響が外に出る操作はaskで人が確認する
  • 機密ファイルとロックファイルはdenyで触れないようにする
  • rm -rfのdenyは、あくまでガードレール。確実に守るにはSandboxを併用する

個人だけが追加したい許可は、gitに入れない.claude/settings.local.jsonに書きます。ただし、チームのdenyは個人設定のallowで上書きできません。

設定が効いているか確認する

確認コマンド
# 権限ルールの一覧と、各ルールの出どころの設定ファイルを表示(追加・削除もできる)
/permissions

# 読み込まれている設定ファイル(Setting sources)を表示
/status

# 設定ファイルの不備や、無効なルールを診断(ターミナルで実行)
claude doctor

/permissionsで追加・削除したルールは、実行中のターンでも次のツール呼び出しから有効になります。$schemaを付けておくと、エディタ上でキー名の誤り(allowedToolsなど)に気づきやすくなります。なお、リポジトリに含まれる設定ファイルのallowは、フォルダを信頼するまで保留される点にも注意してください。

よくある質問

Qallowに書いていないコマンドは実行できませんか?

Aいいえ。通常のモードでは、確認ダイアログが出るだけです。拒否したい場合はdenyに書くか、dontAskモードを使います。

Qallowとdenyの両方に一致するときは、どちらが優先されますか?

Adenyです。評価はdeny、ask、allowの順で、具体性は関係しません。広いdenyを、狭いallowで例外にすることはできません。

Qsettings.local.jsonでチームのdenyを解除できますか?

Aできません。どこかのレベルでdenyされたものは、他のレベルのallowでは許可できません。

Q「.envを読まないで」とCLAUDE.mdに書くだけでは足りませんか?

A足りません。CLAUDE.mdは強制される設定ではなく、Claudeが読む指示です。確実に拒否するならRead(./.env)のdenyを使います。

QBashのdenyを書けば、そのコマンドは確実に止まりますか?

A確実ではありません。別のパスで呼ぶ、sh -cで包むなどの形には一致しません。確実に制限したい場合は、Sandboxを併用してください。

Q/permissions allow ...のようなコマンドはありますか?

Aありません。/permissionsは対話のダイアログで、そこでルールを追加・削除します。追加したルールは設定ファイルに保存されます。

まとめ

allowは「確認を省く」、askは「毎回確認する」、denyは「止める」ルールです。評価はdeny、ask、allowの順で、最初に一致したものが結果になり、広いdenyは狭いallowで例外にできません。allowに書かなかった操作は禁止されるのではなく、確認が出るだけです。

Bashのdenyは書かれたコマンドの文字列に対するガードレールで、迂回を完全には防げません。機密ファイルはReadのdenyで、確実に守りたい境界はSandboxやHookで守る、と役割を分けて設計すると安全です。設定ファイル同士の優先順位はClaude Codeのsettings.jsonが反映されない原因|User・Project・Localの優先順位、全フィールドの意味はClaude Code settings.json完全リファレンス|全フィールドの意味と.claude/rules/の使い方でも確認できます。