はじめに
Sentryの「公開DSN」(認証不要のエラー送信キー)を1つ悪用するだけで、Claude Code・Cursor・OpenAI Codexといった主要なAIコーディングエージェントを乗っ取れることが、セキュリティ企業Tenet Securityの調査チームTenet Threat Labsの調査で明らかになりました1。この記事では攻撃の仕組みを整理したうえで、Claude Code公式ドキュメントに実在する設定キーだけを使い、実際に手元の settings.json で塞ぐところまで解説します。
この記事で学べること
- 「Agentjacking」と呼ばれる攻撃の仕組みと、なぜ現行のAIエージェントがほぼ確実に騙されるのか
- Claude Codeの
sandbox/permissions設定で、攻撃の実害(認証情報の窃取)を防ぐ具体的な設定 - 自分の
settings.jsonが対策済みかを機械的にチェックするスクリプト
対象読者
- Claude Code・Cursor・Codex等のAIコーディングエージェントを業務で使っている方
- MCP連携(Sentry・GitHub等の外部サービス)をエージェントに組み込んでいる方
TL;DR
- 攻撃者は認証不要の公開Sentry DSNに偽のエラーをPOSTし、「解決策」欄にマルウェア実行コマンドを仕込める
- 開発者がエージェントに「このエラーを直して」と頼むと、エージェントは偽の解決策を鵜呑みにしてコマンドを実行してしまう
- Claude Codeのsandboxは既定では
~/.aws/credentials等の認証情報を読めてしまう。sandbox.credentialsで明示的に塞ぐ必要がある
Agentjacking攻撃の仕組み
Tenet Threat Labsが2026年6月17日に公開した調査によると、攻撃は次の流れで成立します1。
- 公開DSNへの偽エラー送信: Sentryの公開DSN(Webアプリのフロントエンドに埋め込まれる、送信専用の認証不要キー)に対し、攻撃者が任意の内容でエラーレポートをPOSTする
-
「解決策」への命令の埋め込み: エラーの「Resolution(解決策)」欄に、正規のトラブルシューティング手順を装って
npx <malicious-package>のようなコマンド実行指示を紛れ込ませる - 開発者による起動: 開発者がClaude CodeやCursorに「このSentryエラーを直して」と依頼すると、エージェントはSentry連携(MCP経由やSentry API経由)でエラー内容を取得し、埋め込まれた指示を「修正手順」として実行してしまう
- 認証情報の収集・送信: 実行されたコマンドが環境変数・AWS認証情報・SSH鍵・Dockerの設定などを収集し、攻撃者が管理する外部サーバーへ送信する
調査では、受動的な偵察(Censysによるインデックス調査・コード検索・CDN配信スクリプトの抽出)だけで 2,388組織 の有効な注入可能DSNが見つかり、うち71組織はTranco top-1M(アクセス数上位100万ドメイン)にランクインしていました1。検証用に用意した100件以上のAIエージェント実行環境(Claude Code・Cursor・Codexを含む4系統以上)に対して注入したところ、sandbox環境・クラウド・GCEコンテナ・WSLを問わず 85%の確率で攻撃が成功 したと報告されています1。
なぜエージェントは騙されるのか
根本原因は、AIエージェントが「読むべき情報(データ)」と「実行すべき指示(コマンド)」を構造的に区別できない点にあります。これは典型的なプロンプトインジェクションの一種で、Microsoftも同時期に類似の問題(MCPツールのメタデータ書き換えによるエージェント誘導)を注意喚起しています2。
Sentryのケースが特に強力なのは、開発者自身が「このエラーを直して」と 自発的に エージェントへ調査・修正を依頼する点です。攻撃者が何かを送りつけて実行させるのではなく、開発者の正当なワークフロー(バグ修正)そのものが攻撃の起点になるため、不審な操作という自覚が生まれにくくなっています。
Claude Codeで実際に塞ぐ設定
Tenet Securityはこの攻撃クラス向けに、Cursor・Claude Code向けのhardening configを公開しています(tenet-security/agent-jackstop)3。ここでは同リポジトリの防御方針を踏まえつつ、Claude Code公式ドキュメントに実在するキーだけで構成した設定を示します4。
防御の要点は「エージェントに攻撃コマンドを実行させないこと」と「万一実行されても認証情報を読ませないこと」の二段構えです。
// .claude/settings.json(プロジェクト単位。組織全体で強制するなら managed-settings.json へ)
{
"permissions": {
"ask": [
"Bash(npx *)",
"Bash(npm exec *)",
"Bash(curl *)",
"Bash(wget *)",
"Bash(aws *)",
"Bash(gcloud *)",
"Bash(gh auth *)"
]
},
"sandbox": {
"enabled": true,
"filesystem": {
"denyRead": ["~/.ssh", "~/.aws", "~/.config/gcloud", "~/.docker/config.json"]
},
"credentials": {
"files": [
{ "path": "~/.aws/credentials", "mode": "deny" },
{ "path": "~/.ssh", "mode": "deny" }
],
"envVars": [
{ "name": "GITHUB_TOKEN", "mode": "deny" },
{ "name": "NPM_TOKEN", "mode": "deny" }
]
},
"network": {
"allowedDomains": ["registry.npmjs.org", "*.npmjs.org", "pypi.org"]
}
}
}
各設定の役割は次のとおりです。
| 設定キー | 役割 |
|---|---|
permissions.ask |
npx や curl 等、Sentryの「解決策」に仕込まれやすいコマンドを実行前に必ず確認させる |
sandbox.filesystem.denyRead |
サンドボックス化されたBashコマンドから認証情報ディレクトリの読み取りを禁止する |
sandbox.credentials |
認証情報ファイル・環境変数を個別に保護し、コマンドが実行されても資格情報自体を渡さない |
sandbox.network.allowedDomains |
攻撃者が指定する未知のドメインへの通信(データ持ち出し)を既定で拒否する |
組織全体に強制したい場合は、managed-settings.json に sandbox.enabled: true と sandbox.allowUnsandboxedCommands: false を設定し、開発者が緩めることができないようにします4。
著者視点の発見ポイント
今回リサーチしていて意外だったのは、Claude Code公式ドキュメントに書かれているsandboxの既定動作でした。sandboxを有効化しただけでは、~/.aws/credentials や ~/.ssh はデフォルトで読み取り可能なままです4。sandboxのファイルシステム隔離は「書き込み」を現在の作業ディレクトリに制限するのが既定動作であり、「読み取り」はホームディレクトリの認証情報を含めてほぼ全体が対象になります。「sandboxを有効にしたから安全」という思い込みは、sandbox.credentials や filesystem.denyRead を明示しない限り成立しません。
もうひとつの発見は、sandboxが制御するのはBashツールの子プロセスのみで、MCPツール呼び出し自体は別の権限系統(permissions)で管理されている点です4。Sentry連携がMCP経由かAPI直結かに関わらず、実際の被害は「取得した指示をBashで実行する」段階で発生します。つまりAgentjacking対策の本丸は「MCPを塞ぐこと」ではなく「取得した情報をコマンドとして実行させないこと」にあり、上記の permissions.ask と sandbox.credentials の組み合わせがその防御ラインになります。
設定を確認するチェックスクリプト
以下のスクリプトは、プロジェクトの .claude/settings.json に上記の主要な対策が含まれているかを機械的に確認します。git clone せずとも手元の設定ファイルにそのまま使えます。
#!/usr/bin/env python3
"""Claude Code settings.json の Agentjacking 対策チェック"""
import json
import sys
from pathlib import Path
REQUIRED_ASK_PATTERNS = ["Bash(npx", "Bash(curl", "Bash(aws"]
REQUIRED_DENY_PATHS = [".ssh", ".aws"]
def check(settings_path: str) -> int:
data = json.loads(Path(settings_path).read_text())
ask = data.get("permissions", {}).get("ask", [])
sandbox = data.get("sandbox", {})
deny_read = sandbox.get("filesystem", {}).get("denyRead", [])
cred_files = [c.get("path", "") for c in sandbox.get("credentials", {}).get("files", [])]
missing = []
if not sandbox.get("enabled"):
missing.append("sandbox.enabled が true になっていません")
for pattern in REQUIRED_ASK_PATTERNS:
if not any(pattern in a for a in ask):
missing.append(f"permissions.ask に {pattern}...) がありません")
for path in REQUIRED_DENY_PATHS:
if not any(path in d for d in deny_read) and not any(path in c for c in cred_files):
missing.append(f"{path} を denyRead / credentials で保護していません")
if missing:
print("NG:")
for m in missing:
print(f" - {m}")
return 1
print("OK: Agentjacking向けの主要設定が揃っています")
return 0
if __name__ == "__main__":
target = sys.argv[1] if len(sys.argv) > 1 else ".claude/settings.json"
sys.exit(check(target))
python3 check_agentjacking_defense.py .claude/settings.json のように実行すると、上記4項目(sandbox有効化・危険コマンドのask化・SSH/AWS認証情報の保護)が欠けている箇所を一覧できます。
まとめ
- Agentjackingは「開発者自身がエージェントに調査・修正を依頼する」正当な操作を起点にするため、既存の異常検知では気付きにくい
- Claude Codeのsandboxは既定で認証情報ディレクトリを読めてしまうため、
sandbox.credentials/filesystem.denyReadの明示設定が必須 -
permissions.askでnpx/curl等の実行前確認を挟むだけでも、被害の連鎖を大きく断ち切れる
Sentry連携に限らず、外部サービスから取得したテキストを「指示」として実行しうる構成全般に同じリスクがあります。エージェントに外部データを読ませる連携を追加するたびに、今回の設定を振り返ることをおすすめします。
参考リンク
- Agentjacking: Coding Agents with Fake Sentry Errors — Tenet Threat Labs(2026-06-17)
- Securing AI agents: When AI tools move from reading to acting — Microsoft Security Blog(2026-06-30)
- tenet-security/agent-jackstop — GitHub
- Configure the sandboxed Bash tool — Claude Code Docs
-
Agentjacking: Coding Agents with Fake Sentry Errors — Tenet Threat Labs(2026-06-17、Ron Bobrov, Barak Sternberg, Nevo Poran) ↩ ↩2 ↩3 ↩4
-
Securing AI agents: When AI tools move from reading to acting — Microsoft Security Blog(2026-06-30) ↩
-
tenet-security/agent-jackstop — GitHub ↩
-
Configure the sandboxed Bash tool — Claude Code Docs ↩ ↩2 ↩3 ↩4