Skip to main content
友田 陽大
本番LLMアプリ実装(信頼できるAI)
生成AI
LLM
セキュリティ
OWASP
プロンプトインジェクション
RAG
AIエージェント
AI駆動開発
アーキテクチャ設計

Production LLM/AI App Security Guide 2026 | OWASP LLM Top 10 & Prompt Injection Defense

A security guide for developers shipping generative-AI/LLM apps to production, aligned with the OWASP Top 10 for LLM Applications (2025) and NIST AI RMF. Defend against prompt injection (direct/indirect), RAG data leakage, excessive agent permissions, and improper output handling — with real TypeScript/Zod/Vercel AI SDK code and defense-in-depth architecture.

Published
Reading time
13 min read
Author
友田 陽大
Share

More and more teams are putting generative AI into production. But "is the model smart?" and "is the app secure?" are entirely separate questions. Securing a production LLM app is not solved by clever prompting. Bottom line up front: it comes down to three architectural principles.

  1. Don't trust the model's output (zero trust) — treat output as "another user's input" and always validate it before passing it downstream.
  2. Minimize tool (integration) permissions — never hand an agent destructive execution rights in the first place.
  3. Require human approval for high-impact actions — refunds, deletions, transfers, and permission changes go through deterministic code and a human gate.

This article is written from the standpoint of running generative-AI pipelines in production while specializing in application security (Next.js × Supabase app defense and threat modeling). Aligned with the OWASP Top 10 for LLM Applications (2025) and NIST AI RMF, it shows only mitigations that work in production, with real TypeScript / Zod / Vercel AI SDK code. Not abstractions — designs you can put in tomorrow's PR.

⚠️ The attack examples here are limited to conceptual explanation for understanding the defenses; no complete, weaponizable payloads are included. Always test in an environment you own.

Why LLM app security is "different"

Traditional web security draws a clear boundary between "untrusted input (the user)" and "trusted code (us)", and validates/escapes at that boundary. In LLM apps, three of those assumptions break.

  • It's not deterministic. The same input can yield different output. You cannot build a complete whitelist that "only allows this pattern."
  • You can't trust the output. The model's output is, in effect, something the user can indirectly steer via the prompt. Trusting LLM output and flowing it downstream is the same as passing user input to SQL without validation.
  • Trust boundaries blur. RAG pours external documents, and agents pour tool results, into the prompt one after another. Without deliberate design, "how far is trusted context?" is undefined.

So defense is not about "how to make the model smarter." It's about treating the model as a probabilistic, untrusted component, and guaranteeing safety with the architecture around it. That's the single thread running through this article.

OWASP LLM Top 10 (2025) at a glance

First, the big picture. Details and defensive code for each item follow.

IDItem (2025)In one linePrimary defense
LLM01Prompt InjectionHijack the model's behavior via inputZero-trust output + least-privilege tools + human approval
LLM02Sensitive Information DisclosureSecrets/PII leak via outputLeast-privilege access, input/output sanitization
LLM03Supply ChainModel/data/dependency supply-chain compromiseVet suppliers, SBOM, signature/hash verification
LLM04Data and Model PoisoningPoisoned training/fine-tuning data, backdoorsProvenance tracking (ML-BOM), data validation, DVC
LLM05Improper Output HandlingPassing output downstream unvalidatedZero-trust output validation, context-aware encoding
LLM06Excessive AgencyExcessive function/permission/autonomy of agentsLeast privilege, propose/execute split, human approval
LLM07System Prompt LeakageSecrets in the system prompt leakKeep secrets out of the prompt, external guardrails
LLM08Vector and Embedding WeaknessesWeaknesses in RAG retrieval/embeddingsAuthorized retrieval, provenance validation, classification
LLM09MisinformationMisinformation from hallucinationRAG, cross-verification, human fact-checking
LLM10Unbounded ConsumptionDoS/cost blow-up from excessive inferenceInput caps, rate limiting, consumption monitoring

Key changes in the 2025 edition (per OWASP official):

  • LLM07 System Prompt Leakage was newly added (real-world exploits became common).
  • LLM08 Vector and Embedding Weaknesses was newly added (production adoption of RAG and embeddings grew).
  • The old Model Denial of Service was expanded into LLM10 Unbounded Consumption, now including resource management and unexpected cost.
  • LLM06 Excessive Agency was expanded given the spread of agentic architectures.

Below we go deep on the highest-impact items for practitioners — LLM01 / LLM05 / LLM06 / LLM08 / LLM10 — with defensive code.

LLM01: Prompt Injection — input filters "fundamentally" can't stop it

Prompt injection tops the LLM Top 10 and is the most misunderstood item. "You can prevent it by trying hard with a filter that blocks bad input" is wrong.

OWASP's LLM01 states it clearly: given that the model operates under essentially stochastic influence, it is unclear whether a fool-proof method of preventing prompt injection exists. In other words, input filtering is a mitigation, not a cure. Make that the starting point of your design.

Direct vs. indirect injection

  • Direct — the user tries to override the model's role directly via the prompt, e.g., "ignore all previous instructions and…".
  • Indirect / cross-domain — malicious instructions are planted inside content the model ingests from external sources (web pages, files, documents retrieved via RAG, tool results, instructions hidden in images). The user themselves may be harmless, yet the "data" the model reads has been poisoned by an attacker.

The more you use agents and RAG, the more the attack surface expands from direct input to "everything the model touches" — this is the backdrop for RAG (LLM08) becoming its own item in the 2025 edition.

Defense in depth: protect at "output and tools," not input

You of course do what you can on the prompt side (state the role, declare the expected output format). But that's only the first layer. The real battleground is the boundary of the model's output and tool execution.

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

The fix is to treat the model as "another untrusted user" and to constrain the output with types (see LLM05 below). And the most effective move is simply not letting the model do dangerous things in the first place — narrowing tool permissions (LLM06). With these two layers, even if injection occurs, you keep the "blast radius" small.

LLM05: Improper Output Handling — validate output with "zero trust"

LLM05 (Improper Output Handling) happens when you pass the model's output to other components (DB, shell, HTML, another API) without validation. In OWASP's words: treat the model like any other user, adopting a zero-trust approach.

With Vercel AI SDK's generateObject and Zod, you can enforce the "type and value range" of the output at the boundary. The key point here: even after schema validation passes, re-check business rules in deterministic code.

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が残高を超える返金を提案しました");
}

If you pass output to SQL, HTML, or a shell, always insert context-aware encoding (HTML escaping, parameterized queries). This is exactly the same as traditional injection defense, and it falls out naturally once you treat "LLM output as a string an attacker may be able to control." For raising the reliability of structured output, see designing reliable structured output.

LLM06: Excessive Agency — separate "propose" from "execute"

The moment you give an agent tools to "issue a refund," "delete a file," or "send an email," prompt injection turns into actual damage. OWASP calls this Excessive Agency and decomposes the cause into excessive functionality, excessive permissions, and excessive autonomy.

The recommended defenses are clear: (1) minimize tools and their functionality to only what is required, (2) least privilege, (3) human-in-the-loop approval for high-impact actions.

The crux of the design is to never hand the agent execution rights. Let the agent go as far as "proposing," and perform the actual destructive operation on the code side, through deterministic business rules and human approval.

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 });
}

To add context, this "propose/execute separation + idempotency key" is not a new idea. It's exactly the reliability and idempotency design I've used to keep production double-charges at zero on a payments platform. Security in the LLM-agent era is nearly continuous with the reliability design of distributed systems — you surround a probabilistic, untrusted component (the LLM) with deterministic guards. For agent design in general, see designing AI-agent tool execution.

LLM08 / LLM02: RAG data leakage — embed authorization into "retrieval"

RAG (retrieval-augmented generation) is an effective way to reduce misinformation (LLM09), but it brings two new attack surfaces.

  1. Indirect prompt injection (LLM08) — malicious instructions are planted in the retrieved documents.
  2. Data leakage from weakly-authorized retrieval (LLM02) — the vector search lacks a permission filter, so confidential documents of other tenants or departments end up in the context.

Case (2) especially happens all the time in production. "Clever prompts" can't stop it. Embedding authorization into the search query itself is the only correct answer.

// アンチパターン:全社ナレッジをそのまま検索 → テナント越境・情報漏洩
// 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");

The important thing here is not to misread this as "wrapping in <context> tags prevents injection." The wrapper only helps convey the intent that "this is data, not instructions" to the model. The real cure for indirect injection is still to contain the damage with LLM05 (output validation) + LLM06 (least-privilege tools). RAG authorization design is also covered in production RAG with pgvector.

LLM07: System Prompt Leakage — don't put secrets in the prompt

A new item in the 2025 edition. Design your system prompt on the assumption that it will leak. Attackers often succeed in making the model dump its prompt, and if it contains API keys, DB names, internal roles, or authorization logic, that becomes a foothold for the next attack.

There are two principles:

  • Move secrets out of the prompt. Put API keys and DB connection info in environment variables or a secrets manager — not in the prompt.
  • Don't delegate security controls to the prompt. Do not implement authorization like "only allow this operation when the user is admin" through prompt instructions. Privilege separation and authorization are enforced deterministically outside the LLM. Guardrails (independent systems that inspect input/output) are a valuable extra layer, but they are not a substitute for deterministic authorization.

LLM10: Unbounded Consumption — stop DoS and cost blow-up

An item that expands the old "Model DoS." Because each LLM request is expensive, abuse immediately turns into financial damage (cost blow-up) and service degradation. The countermeasures are nearly identical to web API defense: rate limiting, input caps, output-token caps, timeouts, and monitoring.

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), // 応答が返らないときは止める
});

And observability. Record token consumption, latency, error rate, and refusal rate per user, and detect anomalous consumption. The design of logs and traces builds on production observability with OpenTelemetry.

Framework crosswalk: OWASP × NIST × MITRE × SAIF

The "OWASP LLM Top 10" is an implementation list for developers. For organizational risk management and a broader threat model, use these alongside it. They are authoritative primary sources that AI answers (AI Overview, ChatGPT, etc.) tend to cite.

PurposeFrameworkPositioning
App-implementation vulnerability checkOWASP Top 10 for LLM Apps 2025The implementation-risk list developers use directly (this article's axis)
Organizational AI risk managementNIST AI RMF 1.0 (AI 100-1)Governance. The four functions: Govern/Map/Measure/Manage
Generative-AI-specific riskNIST GenAI Profile (AI 600-1)The generative-AI profile of the AI RMF
Dictionary of adversarial ML tacticsMITRE ATLASAn ATT&CK-style matrix of attack techniques
Design principles / org rolloutGoogle SAIFA framework for secure AI adoption

For how to run threat modeling itself, see threat modeling with STRIDE; for the secure-coding foundation, see secure coding aligned with NIST SSDF / OWASP ASVS.

Adoption checklist (before going to production)

The minimal set you can use starting tomorrow. It distills the OWASP LLM Top 10 into "design-review viewpoints."

  • Output validation (LLM05) — Is LLM output validated for type and value range with a Zod schema, and are business rules re-checked in deterministic code?
  • Least-privilege tools (LLM06) — Have you avoided handing agents direct destructive execution rights? Did you separate "propose" from "execute"?
  • Human approval (LLM06) — Do high-impact actions (refund, delete, transfer, permission change) have an approval gate?
  • Idempotency (LLM06) — Do side-effecting tool executions have an idempotency key so retries don't double-execute?
  • RAG authorization (LLM02/LLM08) — Do you embed tenant/visibility filters in the vector search query? Do you treat retrieved documents as untrusted input?
  • Secret separation (LLM07) — Have you kept API keys, DB info, and authorization logic out of the prompt?
  • Rate limiting and token caps (LLM10) — Do you have per-user rate limiting, input-length validation, maxOutputTokens, and timeouts?
  • Observability (LLM10) — Do you record token consumption, latency, and refusal rate per user, and can you detect anomalies?
  • Supply chain (LLM03/LLM04) — Do you vet the sources of models, datasets, and dependencies, and keep provenance (SBOM/ML-BOM)?

Summary

Securing a production LLM app is not special magic. It comes down to one thing: "treat the LLM as a probabilistic, untrusted component, and surround it with deterministic architecture."

  • Prompt injection can't be cured by input filters (LLM01). So validate output with zero trust (LLM05), and keep tools at least privilege with human approval for high-impact actions (LLM06).
  • RAG and agents widen the attack surface. Embed authorization into retrieval (LLM08/LLM02), and move secrets out of the prompt (LLM07).
  • Stop abuse and cost blow-up with rate limiting, token caps, and observability (LLM10).

And much of this is continuous with the reliability design of distributed systems that kept production double-charges at zero on a payments platform. "Building fast" and "being in a defensible state" are compatible when you design correctly.

Frequently asked questions

Can prompt injection be fully prevented?
No. OWASP states plainly that, given the stochastic nature of models, it is unclear whether any fool-proof method of prevention exists (LLM01:2025). That is exactly why defense should not bet on input filtering alone: validate the model's output with zero trust, keep tools at least privilege, and require human approval for high-impact actions — a defense-in-depth stance.
Does using RAG make my app secure?
RAG helps reduce misinformation (LLM09), but it introduces a new attack surface of its own. Indirect prompt injection can smuggle malicious instructions inside retrieved documents, and weakly-authorized retrieval can cause cross-tenant leakage (LLM02/LLM08). Make the authorization filter mandatory in the search query and treat retrieved content as untrusted external input.
Are guardrails (input/output filters) enough on their own?
No. Guardrails — independent checks running outside the LLM — are a valuable layer, but critical controls like privilege separation and authorization must be enforced deterministically outside the model, not via prompt instructions (LLM07). Never delegate security to the prompt.
What are the main changes in OWASP LLM Top 10 2025?
'System Prompt Leakage (LLM07)' and 'Vector and Embedding Weaknesses (LLM08, RAG)' were newly added; the old 'Model Denial of Service' was expanded into 'Unbounded Consumption (LLM10)', which now includes resource management and unexpected cost; and 'Excessive Agency (LLM06)' was expanded given the rise of agentic architectures.
Where should I start securing my own LLM app?
First, make trust boundaries visible with threat modeling. Then prioritize four things: validating LLM output (Zod schema), least-privilege tools, human approval for destructive actions, and rate limiting with token caps. Using the OWASP LLM Top 10 as an adoption checklist gives you coverage.
What's the most important principle for AI-agent permission design?
Least privilege and separating 'propose' from 'execute'. Let the agent go only as far as reading and proposing; perform the actual destructive operation on the code side, gated by deterministic business rules and human approval, with an idempotency key (LLM06). The key is to never hand the agent direct execution rights.

References

友田

友田 陽大

Developer of a METI Minister's Award–winning product. With TypeScript + Python + AWS, I deliver SaaS, industry DX, and production-grade generative AI (RAG) end to end — from requirements to infrastructure and operations — single-handedly.

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.

Available for both project-based (contract) and advisory engagements. Start with a free 30-minute consult.

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

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

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

Also worth reading