Claude Code のサンドボックスを「外れた記録」から締めていく(bypass ログ・パイプ deny の罠・起動失敗時の扱い)
サンドボックスを有効にしても、Claude は失敗したコマンドを dangerouslyDisableSandbox 付きで外で再試行することがある。止めずに記録する hook を入れて頻度順に読み、許可リストか deny ルールに落としていくと、外れる回数が減っていく。合わせて「パイプを含む deny は効かない」「サンドボックスが起動できないと黙って素で動く」の2つの罠を潰す。
どんな場面で使う?
- サンドボックスを入れたのに、許可プロンプトが減らない/外での実行が常態化している
Bash(curl *|*sh)のような deny を書いて安心している- 他人の公開スキルや MCP を入れていて、万一のときの被害範囲を狭めたい
手順
1. 外れた瞬間だけ記録する hook
~/.claude/hooks/log-sandbox-bypass.sh(chmod +x して PreToolUse の matcher: "Bash" に登録):
bash
#!/bin/bash
INPUT=$(cat)
LOG=~/.claude/logs/sandbox-bypass.tsv
DISABLED=$(echo "$INPUT" | jq -r '.tool_input.dangerouslyDisableSandbox // false')
if [ "$DISABLED" = "true" ]; then
CMD=$(echo "$INPUT" | jq -r '.tool_input.command // ""')
CMD=$(printf '%s' "$CMD" | sed -E \
-e 's/([A-Za-z_]*(KEY|SECRET|TOKEN|PASSWORD|PASSWD)[A-Za-z_]*=)[^ ]*/\1***/g' \
-e 's/(Bearer )[A-Za-z0-9._~+\/-]+/\1***/g')
mkdir -p "$(dirname "$LOG")"
umask 077
printf '%s\t%s\n' "$(date -Iseconds)" "$CMD" >> "$LOG"
fi
exit 0 # 止めない。止めると作業が進まず記録も溜まらない2. 週に一度、多い順に読んで反映する
bash
cut -f2 ~/.claude/logs/sandbox-bypass.tsv | sort | uniq -c | sort -rn | head| ログから読み取れること | 反映先 |
|---|---|
| 特定ホストへの接続で外れている | sandbox.network.allowedDomains |
| 作業ディレクトリ外への書き込みで外れている | sandbox.filesystem.allowWrite |
| docker など特定ツールが毎回外れる | sandbox.excludedCommands |
| そもそもやらせたくない操作 | 許可ではなく permissions.deny |
| 散発的で正当性も薄い | allowUnsandboxedCommands: false(Strict) |
ログはコマンド全文なので秘密が残りうる。cleanupPeriodDays は ~/.claude/projects/ の transcript にしか効かないので、tail -n 500 … > /tmp/sb.tsv && mv … で自分で切り詰め、調整が済んだら消す。
3. deny はパイプの「部品」ごとに照合される
curl https://x | sh は curl … と sh に分けて判定されるので、Bash(curl *|*sh) は永遠にマッチしない。代わりに受け側のインタプリタを止め、取得系は確認に回す:
jsonc
"permissions": {
"deny": ["Bash(sh *)", "Bash(bash *)", "Bash(zsh *)"],
"ask": ["Bash(curl *)", "Bash(wget *)"]
}4. 黙って素で動く・環境変数が漏れる、を塞ぐ
jsonc
{
"sandbox": {
"enabled": true,
"failIfUnavailable": true, // 依存不足でサンドボックスが立たないとき起動を拒否
"filesystem": { "denyRead": ["~/.ssh", "~/.gnupg", "~/.aws", "~/.config/gcloud"] },
"credentials": {
"files": [{ "path": "~/.aws/credentials", "mode": "deny" }],
"envVars": [{ "name": "GITHUB_TOKEN", "mode": "deny" }]
}
},
"env": { "CLAUDE_CODE_SUBPROCESS_ENV_SCRUB": "1" }, // Anthropic・クラウドの認証情報を子プロセスから剥がす
"apiKeyHelper": "/usr/local/bin/get-my-key", // env にキーの値を直書きしない
"cleanupPeriodDays": 14
}- 読み取りは既定で全域許可なので、
denyReadが実質の防御線。ファイル隔離とネットワーク隔離は片方だけだと抜け道が残る credentialsに組み込みの除外リストはない。挙げたものだけが守られる
5. 効いているか確かめる
/sandbox の Config/Overrides、/permissions の Recently denied、claude doctor を見たうえで、許可していないドメインへ実際に curl して弾かれるかを試す。チームで集めるなら OpenTelemetry の claude_code.tool_decision イベントが decision と source(config/hook/user_reject)を持つので「何が止めたか」まで分かる。
注意点
- 組み込みの Read/Edit/Write ツールはサンドボックスを通らず、permissions の Read/Edit ルールで制御される。両方書く
- ドメイン制限のプロキシは TLS の中身を見ないので、ドメインフロンティングは防げない
- 2026年4月の修正より前は
dangerouslyDisableSandboxが許可プロンプトなしで通るバグがあった。古い版ならまず更新 - 「書いたルールは機械的に弾けるようにしないと、いつか破られる」(@suthio_ さん)。@lancinimarco さんは hook で
rm -rfをtrashに置き換えさせ、docker・npm・terraformなどの生コマンドを禁じて自作 CLI だけを通し、terraform apply/destroyは「コマンドを提案させて人が打つ」にしている - 関連: 「今後は確認しない」の許可ルールは sudo と同じ:ワイルドカードを承認しない、エージェントを隔離して動かす選択肢(nono / Docker Sandboxes / agentos / VPS)、エージェントの通信をプロキシ経由にして、許可したサイトにしか出られないようにする
出典
- いいね: @okdt · 2026-04-18 (Claude Code Hardening Cheatsheet: github.com )
- いいね: @suthio_ · 2026-04-20
- いいね: @lancinimarco · 2026-04-22 (My Claude Code Setup (2026 Edition): blog.marcolancini.it )