Chuyển đến nội dung chính
友田 陽大
本番LLMアプリ実装(信頼できるAI)
生成AI
LLM
セキュリティ
OWASP
プロンプトインジェクション
RAG
AIエージェント
AI駆動開発
アーキテクチャ設計

本番LLM/AIアプリのセキュリティ実装ガイド 2026|OWASP LLM Top 10とプロンプトインジェクション対策

生成AI/LLMを本番投入する開発者向けに、アプリ層のセキュリティをOWASP LLM Top 10(2025)とNIST AI RMFに準拠して解説。プロンプトインジェクション(直接/間接)、RAGの情報漏洩、AIエージェントの過剰権限、出力の不適切処理を、TypeScript/Zod/Vercel AI SDKの実コードと多層防御の設計で対策します。

Published
Reading time
16 min read
Author
友田 陽大
Chia sẻ

生成AIを本番のプロダクトに載せる企業が一気に増えました。しかし「モデルが賢いか」と「そのアプリが安全か」は、まったく別の問題です。本番のLLMアプリのセキュリティは、プロンプトを工夫することでは解決しません。 結論から言えば、次の3つのアーキテクチャ原則にほぼ集約されます。

  1. LLMの出力を信頼しない(ゼロトラスト) — 出力は「別のユーザー入力」として、下流に渡す前に必ず検証する。
  2. ツール(外部連携)の権限を最小化する — エージェントに破壊的な実行権限そのものを渡さない。
  3. 高影響の操作は人間が承認する — 返金・削除・送金・権限変更などは、決定的なコードと人間のゲートを通す。

この記事は、生成AIパイプラインを本番で運用し、かつアプリケーションセキュリティ(Next.js × Supabase のアプリ防御脅威モデリング)を専門にしてきた立場から、OWASP Top 10 for LLM Applications(2025年版)NIST AI RMF に準拠して、TypeScript / Zod / Vercel AI SDK の実コードで「本番で効く」対策だけを示します。抽象論ではなく、明日のPRに入れられる設計です。

⚠️ 本記事の攻撃例は、防御設計を理解するための概念説明に限定し、悪用可能な完全なペイロードは記載しません。検証は必ず自分が管理する環境で行ってください。

なぜLLMアプリのセキュリティは「従来と違う」のか

従来のWebアプリのセキュリティは、「信頼できない入力(ユーザー)」と「信頼できるコード(自分たち)」の間に明確な境界を引き、その境界で検証・エスケープする——という考え方で成立してきました。

LLMアプリでは、この前提が3つ崩れます。

  • 決定論的でない。 同じ入力でも出力が変わりうる。「このパターンだけ弾く」という完全なホワイトリストが作れない。
  • 出力を信頼できない。 LLMの出力は、実質的にユーザーが(プロンプト経由で)間接的に操作できる。LLMの出力を信頼して下流に流すのは、ユーザー入力を無検証でSQLに渡すのと同じです。
  • 信頼境界が曖昧になる。 RAGで外部文書を、エージェントでツールの実行結果を、プロンプトに次々と流し込む。どこまでが「信頼できるコンテキスト」なのかが設計しないと決まらない。

だから防御は「モデルをどう賢くするか」ではなく、「モデルを確率的で信頼できない一部品として扱い、その周りのアーキテクチャで安全を担保する」方向に置きます。これが本記事を貫く一本の軸です。

OWASP LLM Top 10(2025)早見表

まず全体像です。各項目の詳細と防御コードはこの後で扱います。

ID項目(2025)一言でいうと主要な防御
LLM01Prompt Injection入力でモデルの挙動を乗っ取る出力ゼロトラスト+ツール最小権限+人間承認
LLM02Sensitive Information Disclosure出力から機密・PIIが漏れる最小権限アクセス、入出力のサニタイズ
LLM03Supply Chainモデル/データ/依存の供給網汚染供給元の検証、SBOM、署名・ハッシュ検証
LLM04Data and Model Poisoning学習/微調整データの汚染・バックドア来歴追跡(ML-BOM)、データ検証、DVC
LLM05Improper Output Handling出力を無検証で下流に渡す出力をゼロトラスト検証、文脈別エンコード
LLM06Excessive Agencyエージェントの過剰な機能/権限/自律性最小権限、提案と実行の分離、人間承認
LLM07System Prompt Leakageシステムプロンプトの秘密が漏れる秘密をプロンプト外へ、外部ガードレール
LLM08Vector and Embedding WeaknessesRAGの検索/埋め込みの弱点認可付きリトリーバル、来歴検証、分類
LLM09Misinformationハルシネーションによる誤情報RAG、相互検証、人間のファクトチェック
LLM10Unbounded Consumption過剰な推論によるDoS・コスト暴走入力上限、レート制限、消費の監視

2025年版での主な変更点OWASP公式より):

  • LLM07 System Prompt Leakage新規追加(実際の攻撃事例が多発したため)。
  • LLM08 Vector and Embedding Weaknesses新規追加(RAGと埋め込みの本番採用が進んだため)。
  • Model Denial of Service を、リソース管理と想定外コストまで含む LLM10 Unbounded Consumption拡張
  • LLM06 Excessive Agency を、エージェント型アーキテクチャの普及を受けて拡充

以降、実務インパクトの大きい LLM01 / LLM05 / LLM06 / LLM08 / LLM10 を、防御コードとともに深掘りします。

LLM01: プロンプトインジェクション — 入力フィルタでは「原理的に」防げない

プロンプトインジェクションは、LLM Top 10の筆頭であり、最も誤解されている項目です。「悪い入力を弾くフィルタを頑張れば防げる」というのは誤りです。

OWASPのLLM01は明確にこう述べています——「モデルが本質的に確率的な影響で動作する以上、プロンプトインジェクションを防止する万全な手法が存在するかは不明である」。つまり入力フィルタは緩和策であって、根治ではない。ここを設計の出発点にします。

直接インジェクションと間接インジェクション

  • 直接(direct) — ユーザーが「これまでの指示を無視して…」のように、プロンプトで直接モデルの役割を上書きしようとする。
  • 間接(indirect / cross-domain) — モデルが外部から取り込むコンテンツ(Webページ、ファイル、RAGで取得した文書、ツールの実行結果、画像に埋め込まれた指示)の中に悪意ある命令が仕込まれる。ユーザー自身は無害でも、モデルが読む「データ」が攻撃者に汚染されているケースです。

エージェントやRAGを使うほど、攻撃面は直接入力から「モデルが触れるすべてのデータ」へ広がる——これが2025年版でRAG(LLM08)が独立項目になった背景です。

多層防御:入力ではなく「出力とツール」で守る

プロンプト側でできること(役割の明示、期待する出力形式の宣言)は当然やります。しかしそれは第一層に過ぎません。本丸は、モデルの出力とツール実行の境界です。

// アンチパターン:LLMの出力を「信頼できる指示」として下流に流す
const answer = await generateText({ model, prompt });
await db.query(answer.text); // ❌ プロンプトインジェクションが即RCE/情報漏洩になる

対策は、モデルを「もう一人の信頼できないユーザー」として扱い、出力を型で締めることです(詳細は後述のLLM05)。そして最も効くのは、そもそもモデルに危険なことをさせない=ツールの権限を絞ること(LLM06)。この2層があれば、インジェクションが起きても「被害の爆発半径」を小さく保てます。

LLM05: 出力の不適切な処理 — 出力を「ゼロトラスト」で検証する

LLM05(Improper Output Handling)は、LLMの出力を検証せずに他コンポーネント(DB・シェル・HTML・別API)へ渡すことで起きます。OWASPの言葉を借りれば「モデルを他のユーザーと同様に扱い、ゼロトラストで接する」。

Vercel AI SDK の generateObject と Zod を使えば、出力の「型と値域」を境界で強制できます。ここでのポイントは、スキーマ検証を通っても、業務ルールは決定的コードで再確認することです。

import { generateObject } from "ai";
import { z } from "zod";

// LLM出力は「別のユーザー入力」= ゼロトラスト。スキーマで境界を締める。
const RefundDecision = z.object({
  action: z.enum(["approve", "reject", "escalate"]),
  amountJpy: z.number().int().min(0).max(50_000), // 上限を「型」で強制する
  reason: z.string().max(200),
});

const { object } = await generateObject({
  model: "anthropic/claude-opus-4-8",
  schema: RefundDecision,
  prompt,
});

// ここまで来た object は「形式的に」検証済み。だが業務ルールは別問題。
// プロンプトインジェクションで action=approve に誘導されても、
// 決定的なコードが最終防衛線になる。
if (object.action === "approve" && object.amountJpy > refundableBalance(order)) {
  throw new PolicyError("LLMが残高を超える返金を提案しました");
}

出力をSQL・HTML・シェルに渡すなら、文脈別のエンコード(HTMLエスケープ、パラメータ化クエリ)を必ず挟みます。これは従来のインジェクション対策とまったく同じで、**「LLMの出力は攻撃者が操作できるかもしれない文字列」**として扱えば自然に導けます。構造化出力の信頼性を上げる設計は structured output の信頼性設計 で詳しく扱っています。

LLM06: 過剰なエージェンシー — 「提案」と「実行」を分離する

エージェントに「返金する」「ファイルを消す」「メールを送る」ツールを渡した瞬間、プロンプトインジェクションは実際の被害に変わります。OWASPはこれをExcessive Agencyと呼び、原因を過剰な機能・過剰な権限・過剰な自律性の3つに分解しています。

推奨される防御は明快です——(1) ツールと機能を必要最小限に絞る、(2) 最小権限、(3) 高影響アクションは human-in-the-loop(人間承認)

設計の要は「エージェントに実行権限を渡さない」こと。エージェントには「提案」までをさせ、実際の破壊的操作はコード側で、決定的な業務ルールと人間承認を通して行います。

import { tool } from "ai";
import { z } from "zod";

// 破壊的アクションは「実行」せず「提案」を返すツールにする。
// ツール自体は最小権限(読み取り+提案レコード作成のみ)。
const proposeRefund = tool({
  description: "返金の提案を作成する(実行はしない)",
  inputSchema: z.object({
    orderId: z.string().uuid(),
    amountJpy: z.number().int().positive(),
  }),
  execute: async ({ orderId, amountJpy }) => {
    // 副作用は「提案レコードの作成」だけ。ここで実際の送金はしない。
    const proposalId = await createRefundProposal({ orderId, amountJpy });
    return { proposalId };
  },
});

// 高影響アクションは human-in-the-loop(OWASP LLM06 の推奨)。
// 実行は決定的コード側で、業務ルール検証 + 冪等キー付きで行う。
async function executeRefund(proposalId: string, approver: Admin) {
  const p = await getProposal(proposalId);
  assertWithinPolicy(p, approver); // 残高・上限・権限を決定的に再検証
  // 冪等キーで「二重返金」を構造的に防ぐ(分散システムの基本)
  await refundIdempotent(p.orderId, p.amountJpy, { idempotencyKey: p.proposalId });
}

補足すると、この「提案と実行の分離+冪等キー」は新しい発想ではありません。私が決済基盤で本番の二重課金を0件に保つために使ってきた信頼性・冪等性の設計そのものです。LLMエージェント時代のセキュリティは、分散システムの信頼性設計とほぼ地続き——確率的で信頼できないコンポーネント(=LLM)を、決定的なガードで囲う。エージェントの設計全般は AIエージェントのツール実行設計 を参照してください。

LLM08 / LLM02: RAGの情報漏洩 — 「検索」に認可を埋め込む

RAG(検索拡張生成)は、誤情報(LLM09)を減らす有効な手段ですが、それ自体が2つの新しい攻撃面を持ち込みます。

  1. 間接プロンプトインジェクション(LLM08) — 取得した文書に悪意ある指示が仕込まれている。
  2. 認可の甘い検索による情報漏洩(LLM02) — ベクトル検索に権限フィルタが無く、他テナント・他部門の機密文書がコンテキストに混入する。

特に(2)は本番で頻発します。「賢いプロンプト」では防げません。検索クエリそのものに認可を埋め込むのが唯一の正解です。

// アンチパターン:全社ナレッジをそのまま検索 → テナント越境・情報漏洩
// const docs = await vectorStore.search(queryEmbedding, { topK: 8 });

// 正:リトリーバルに「認可フィルタ」を必須で埋め込む。
// アプリ層で後からフィルタするのではなく、検索そのものを認可でスコープする。
const docs = await vectorStore.search(queryEmbedding, {
  topK: 8,
  filter: {
    tenantId: session.tenantId,
    visibility: { $in: session.scopes }, // 閲覧可能な範囲だけを検索対象に
  },
});

// 取得したドキュメントは「信頼できない外部入力」。
// プロンプトに入れる前に来歴を明示し、"指示ではなくデータ" として扱う。
const context = docs
  .map((d) => `<context source="${d.source}">\n${d.text}\n</context>`)
  .join("\n\n");

ここで重要なのは、<context> タグで囲えばインジェクションが防げる、と誤解しないことです。囲みは「これはデータであって指示ではない」という意図をモデルに伝える補助にすぎません。間接インジェクションの根治は、あくまで LLM05(出力検証)+ LLM06(ツール最小権限) で被害を封じ込めることです。RAGの認可設計は pgvectorによる本番RAG でも触れています。

LLM07: システムプロンプトの漏洩 — 秘密をプロンプトに置かない

2025年版の新項目。システムプロンプトは漏れる前提で設計します。攻撃者はしばしばプロンプトを吐き出させることに成功し、そこにAPIキー・DB名・内部ロール・認可ロジックが書かれていれば、次の攻撃の足がかりになります。

原則は2つ。

  • 秘密をプロンプトから外に出す。 APIキーやDB接続情報は環境変数・シークレットマネージャに。プロンプトには置かない。
  • セキュリティ制御をプロンプトに委譲しない。 「ユーザーがadminのときだけこの操作を許可して」といった認可をプロンプトの指示で実現しない。権限分離・認可は、LLMの外で決定的に強制します。ガードレール(入出力を検査する独立システム)は有効な追加層ですが、それは決定的な認可の代わりにはなりません

LLM10: 無制限な消費 — DoSとコスト暴走を止める

旧「Model DoS」を拡張した項目。LLMは1リクエストが高コストなので、濫用は即座に金銭的被害(コスト暴走)とサービス劣化につながります。対策はWebのAPI防御とほぼ同じで、レート制限・入力上限・出力トークン上限・タイムアウト・監視です。

import { Ratelimit } from "@upstash/ratelimit";

// 1) レート制限:濫用・コスト暴走・モデル窃取(大量クエリでの蒸留)を抑止
const { success } = await ratelimit.limit(`llm:${session.userId}`);
if (!success) return new Response("Too Many Requests", { status: 429 });

// 2) 入力サイズと出力トークンを「上限」で締める(=コスト上限)
const { text } = await generateText({
  model: "anthropic/claude-haiku-4-5",
  prompt: clampInput(userPrompt, 4_000), // 入力長を検証してから渡す
  maxOutputTokens: 512, // 出力上限 = コストの上限
  abortSignal: AbortSignal.timeout(15_000), // 応答が返らないときは止める
});

そして可観測性。トークン消費・レイテンシ・エラー率・拒否率をユーザー単位で記録し、異常な消費を検知します。ログとトレースの設計は OpenTelemetryによる本番可観測性 が土台になります。

フレームワーク対応表:OWASP × NIST × MITRE × SAIF

「OWASP LLM Top 10」は開発者向けの実装リストです。組織的なリスク管理や、より広い脅威モデルを扱うには、以下を併用します。AIの回答(AI Overview / ChatGPTなど)でも引用されやすい、権威ある一次情報です。

目的フレームワーク位置づけ
アプリ実装の脆弱性チェックOWASP Top 10 for LLM Apps 2025開発者が直接使う実装リスト(本記事の軸)
組織のAIリスク管理NIST AI RMF 1.0 (AI 100-1)ガバナンス。Govern/Map/Measure/Manage の4機能
生成AI固有のリスクNIST GenAI Profile (AI 600-1)AI RMFの生成AI版プロファイル
敵対的ML攻撃の戦術辞典MITRE ATLASATT&CK型の攻撃手法マトリクス
設計原則・組織展開Google SAIFセキュアなAI導入のフレームワーク

脅威モデリング自体の進め方は STRIDEによる脅威モデリング、セキュアコーディングの土台は NIST SSDF / OWASP ASVS 準拠のセキュアコーディング にまとめています。

導入チェックリスト(本番投入前)

明日から使える最小セットです。OWASP LLM Top 10を「設計レビューの観点」に落とし込みました。

  • 出力検証(LLM05) — LLMの出力はZodスキーマで型・値域を検証し、業務ルールは決定的コードで再確認しているか。
  • ツール最小権限(LLM06) — エージェントに破壊的実行権限を直接渡していないか。「提案」と「実行」を分離したか。
  • 人間承認(LLM06) — 返金・削除・送金・権限変更などの高影響アクションに承認ゲートがあるか。
  • 冪等性(LLM06) — 副作用のあるツール実行に冪等キーがあり、リトライで二重実行しないか。
  • RAGの認可(LLM02/LLM08) — ベクトル検索クエリにテナント/可視性フィルタを埋め込んでいるか。取得文書を「信頼できない入力」として扱っているか。
  • 秘密の分離(LLM07) — APIキー・DB情報・認可ロジックをプロンプトに書いていないか。
  • レート制限とトークン上限(LLM10) — ユーザー単位のレート制限、入力長検証、maxOutputTokens、タイムアウトがあるか。
  • 可観測性(LLM10) — トークン消費・レイテンシ・拒否率をユーザー単位で記録し、異常を検知できるか。
  • サプライチェーン(LLM03/LLM04) — モデル・データセット・依存の供給元を検証し、来歴(SBOM/ML-BOM)を残しているか。

まとめ

本番のLLMアプリのセキュリティは、特別な魔法ではありません。「LLMを、確率的で信頼できない一部品として扱い、決定的なアーキテクチャで囲う」——この一点に尽きます。

  • プロンプトインジェクションは入力フィルタでは根治できない(LLM01)。だから**出力をゼロトラストで検証(LLM05)**し、**ツールを最小権限にして高影響操作は人間が承認(LLM06)**する。
  • RAGとエージェントは攻撃面を広げる。検索に認可を埋め込み(LLM08/LLM02)、秘密はプロンプトの外へ(LLM07)
  • 濫用とコスト暴走は**レート制限・トークン上限・可観測性(LLM10)**で止める。

そしてこれらの多くは、決済基盤で本番二重課金0件を実現してきた分散システムの信頼性設計と地続きです。「速く作る」ことと「守れる状態にする」ことは、正しく設計すれば両立します。

Câu hỏi thường gặp

プロンプトインジェクションは完全に防げますか?
いいえ。OWASPは『モデルの確率的な性質ゆえ、防止の万全な手法があるかは不明』と明言しています(LLM01:2025)。だからこそ防御は入力フィルタ単体に賭けず、LLM出力をゼロトラストで検証し、ツールを最小権限にし、高影響アクションは人間が承認する多層防御に置きます。
RAGを使えばセキュリティは安全になりますか?
RAGは誤情報(LLM09)の低減には有効ですが、それ自体が新たな攻撃面になります。取得したドキュメントに悪意ある指示が混入する『間接プロンプトインジェクション』や、認可の甘い検索によるテナント越境・情報漏洩(LLM02/LLM08)が起きます。検索クエリに認可フィルタを必須化し、取得内容は信頼できない外部入力として扱ってください。
ガードレール(入出力フィルタ)だけで十分ですか?
不十分です。ガードレールはLLMの外側で動く独立した検査として有効な層ですが、権限分離や認可といった重要な制御は、プロンプトの指示ではなくLLMの外で決定的に強制する必要があります(LLM07)。セキュリティをプロンプトに委譲してはいけません。
OWASP LLM Top 10 2025の主な変更点は?
『System Prompt Leakage(LLM07)』と『Vector and Embedding Weaknesses(LLM08、RAG)』が新規追加、旧『Model Denial of Service』はリソース管理と想定外コストを含む『Unbounded Consumption(LLM10)』へ拡張、『Excessive Agency(LLM06)』はエージェント普及を受けて拡充されました。
自社のLLMアプリのセキュリティは何から始めるべきですか?
まず脅威モデリングで信頼境界を可視化し、次に『LLM出力の検証(Zodスキーマ)』『ツールの最小権限化』『破壊的操作の人間承認』『レート制限とトークン上限』の4点を最優先で実装します。OWASP LLM Top 10を導入チェックリストとして使うと網羅性を担保できます。
AIエージェントの権限設計で最も重要な原則は何ですか?
最小権限と『提案と実行の分離』です。エージェントには読み取り+提案までを許可し、実際の破壊的操作は、決定的な業務ルールと人間承認を通したコード側で、冪等キー付きで実行します(LLM06)。エージェントに直接『実行権限』を渡さないことが要点です。

Tài liệu tham khảo

友田

友田 陽大

Người phát triển sản phẩm đoạt Giải thưởng Bộ trưởng (METI). Với TypeScript + Python + AWS, tôi một mình triển khai trọn gói SaaS, chuyển đổi số công nghiệp và AI tạo sinh (RAG) sẵn sàng cho production — từ phân tích yêu cầu đến hạ tầng và vận hành.

I can take on the implementation from this article as an engagement

Security engineering, from design to implementation and operations

Design reviews via threat modeling, correct implementation of crypto and authn/authz, log design and detection (detection engineering), and building an incident-response capability. With experience building — solo × generative AI — a METI Minister's Award B2B SaaS and a payments platform with zero double charges in production, I help get your product to a state where it ships fast and can be defended. Vertical risks that only design can address, I also take on as an audit.

Nhận cả dự án trọn gói lẫn vai trò cố vấn kỹ thuật. Hãy bắt đầu bằng buổi tư vấn miễn phí 30 phút.

最短ルート:カレンダーから直接予約

相談内容が固まっている方は、フォーム送信よりその場で日程を確定する方がスムーズです。下記から空き時間をお選びください。

  • 30分のオンライン無料相談
  • Google Meet / Zoom / Microsoft Teams
  • NDA 商談前締結可・無理な営業はいたしません
無料相談の空き枠を予約する

Cũng đáng đọc