FlawOpen

flawopen.com/リファレンス/PR botのセキュリティ

AI PRレビューbot:Prompt InjectionとSSRF対策

AI PRレビューbot:Prompt InjectionによるSSRFを防ぐ

信頼できないPull Requestの文章がAIツールに与える影響と、botの権限やネットワークを制限する方法を解説します。

セキュリティリスク CWE-1427 / CWE-918 AI・開発ワークフローのセキュリティ

やさしい説明:社内ネットワークの通行証を持つレビュー担当

Pull Requestはレビュー担当に届く荷物のようなものです。中身は役立つことがあっても、会社の方針になるわけではありません。AIレビューbotがプライベートネットワークから任意のURLを取得できると、誤解を招く指示がアクセス先に影響する可能性があります。リスクはツールとネットワークの権限次第です。敵対的な文章を読むだけでネットワーク権限が得られるわけではありません。

基本概念と重要用語

信頼できない入力:
コントリビューターが送るPull Requestのタイトル、説明、コメント、diff、リンク先の内容。
間接Prompt Injection:
信頼できない文章がモデルのコンテキストに入ることでエージェントに影響すること。
SSRFの境界:
サーバー側の取得ツールが投稿者から直接アクセスできない宛先に到達すること。
影響の条件:
アクセス可能な内部サービス、利用できる認証情報、不十分な認可または送信制御も必要です。

仕組みと根本原因

レビューbotは投稿者が制御できる文章と、信頼できるレビュー指示をモデルに渡すことがあります。モデルはどちらからでもツール呼び出しを提案できます。プロンプトや区切り文字はネットワークアクセスを認可しません。アプリがモデル選択の宛先を受け入れ、ネットワーク接続可能なツールがそこへ到達できる場合に限り、SSRFが成立します。クラウドメタデータや認証情報へのアクセスは追加の条件付き影響であり、Prompt Injectionだけで自動的に起こるわけではありません。

条件付きの攻撃経路

1

投稿者がPR内容を制御

外部投稿者はタイトル、説明、コメント、変更テキスト、リンク先に影響を与えられます。

2

エージェントが内容を読む

サービスが一部の未信頼フィールドをモデルのコンテキストへ入れると、回答やツール提案に影響する可能性があります。

3

ツールが到達範囲を決める

任意URLを取得できる場合、runnerはネットワークで許された宛先へ接続できます。そのツールがなければ、このSSRF経路はありません。

4

影響は環境で変わる

内部データや認証情報が露出するには、到達可能なサービス、メタデータ設定、権限、出力制御が許している必要があります。

コード:未検証の呼び出しと認可済み読み取り

無害なモック呼び出し
# INERT DEMO: model-selected URL reaches a mock tool without policy checks
from dataclasses import dataclass

@dataclass
class ToolCall:
    name: str
    arguments: dict[str, str]

class MockFetch:
    def __init__(self):
        self.calls = []

    def __call__(self, url: str) -> str:
        self.calls.append(url)
        return "fixture response"

def dispatch_unchecked(call: ToolCall, tools: dict):
    # The mock records a string; it makes no network request.
    return tools[call.name](**call.arguments)

mock_fetch = MockFetch()
call = ToolCall("fetch_url", {"url": "https://docs.example.invalid/"})
assert dispatch_unchecked(call, {"fetch_url": mock_fetch}) == "fixture response"
assert mock_fetch.calls == ["https://docs.example.invalid/"]
ポリシーで制限した読み取りツール
# SAFE DEMO: the agent receives only a read-only PR capability
from dataclasses import dataclass

@dataclass(frozen=True)
class ReviewContext:
    repository: str
    pull_request: int

@dataclass
class ToolCall:
    name: str
    arguments: dict[str, object]

READ_ONLY_TOOLS = {"read_pr_diff"}

def dispatch_review_call(call: ToolCall, context: ReviewContext, policy, tools):
    if call.name not in READ_ONLY_TOOLS:
        raise PermissionError("Tool is not available to this review")
    expected = {"repository": context.repository,
                "pull_request": context.pull_request}
    if call.arguments != expected:
        raise ValueError("Arguments must match the authorized review")
    if not policy.can_read_pr(context.repository, context.pull_request):
        raise PermissionError("Review principal cannot read this pull request")
    # The injected read_pr_diff function is trusted app code, not model output.
    return tools["read_pr_diff"](**expected)

エンジニアリング強化チェックリスト

関連する実在の脆弱性 — CVE-2023-32786

GitHubのレビュー済みCVE-2023-32786アドバイザリによると、LangChain 0.0.329より前ではPrompt Injectionによって任意URLを取得させ(SSRF)、後続タスクに影響する可能性がありました。修正版は0.0.329です。これは記録されたURL取得の連鎖であり、PR botのインシデントやクラウドメタデータ窃取を証明するものではありません。

主なガイダンスと記録

セキュリティディレクトリを見る → その他のリファレンスを見る →
関連項目 サーバーサイドリクエストフォージェリ(SSRF、CWE-918):仕組みと各言語での防ぎ方 — flawopen.com Node.js fetch()のSSRF対策と安全なURL取得 SSRFとクラウドメタデータ:認証情報のリスクと防御