# Prompt-Injection Defense Guide

Part of the Ship-Safe LLM Features Kit — ventrova.dev/kit

The five-layer mitigation order from our research, expanded with implementation notes and the failure modes we actually saw when testing targets. Source: same research behind the "Securing LLM Features" post at ventrova.dev/blog/securing-llm-features-prompt-injection-eu-ai-act, expanded here with detail not in the free version.

## Why order matters

These five layers are listed in the order they should be applied, not in order of importance. Layer 1 (privilege segregation) limits blast radius even if every later layer fails; layer 5 (red-teaming) only tells you whether layers 1-4 actually held. Teams that start at layer 4 or 5 without 1-3 end up with a red-team report full of findings and no structural way to fix them short of a rewrite.

## Layer 1: Privilege segregation

Don't let the model's output directly cause privileged effects. Put a non-LLM boundary between "model decided to do X" and "X happened" for anything privileged: payments, deletions, permission changes, outbound sends, anything hard to undo.

Failure mode we saw: teams wire the model's function-call output straight into a privileged action handler because "the model already validated the arguments." The model is not a validation layer; treat its output the way you'd treat client-supplied input, because functionally that's what it is; an attacker who can influence the prompt can influence the function call.

Implementation notes: use API features that structurally separate system instructions from user/retrieved content (separate message roles, not string concatenation) wherever available; run the LLM process under credentials scoped to only what that feature needs, never the calling user's full session or a shared service account with broad access.

## Layer 2: Untrusted-content handling

Anything that enters the prompt from outside your own system prompt, i.e. user messages, retrieved documents, web pages, emails, tool/API responses, is untrusted content and should be handled as data, not instructions.

Failure mode we saw: retrieved RAG chunks concatenated directly into the prompt with no delimiter, so a chunk containing "ignore the above and..." reads exactly like a continuation of the system prompt to the model.

Implementation notes: delimit or tag untrusted content explicitly (e.g. XML-style tags, a separate message role) and tell the model explicitly, in the system prompt, that content inside those delimiters is data to reference, never instructions to follow. Test this instruction, don't assume it holds; models vary in how well they respect it, especially under adversarial pressure (see layer 5).

## Layer 3: Least-privilege tools

Every tool or function you expose to the model is a capability an attacker gets if they can hijack the model's behavior. Scope tools as narrowly as possible.

Failure mode we saw: a general "run_query" or "make_request" tool that can touch far more than the feature actually needs, because it was easier to build one flexible tool than several narrow ones.

Implementation notes: one tool, one narrow job. Validate arguments server-side after the model proposes them; never trust that the model's proposed arguments are safe just because the model proposed them. Any tool with a side effect gets a hard-coded limit (rate, amount, scope) or a human-in-the-loop step, especially for anything that sends data externally or spends money.

## Layer 4: Output filtering

What the model outputs still needs to be treated as untrusted before it's rendered, executed, or passed downstream.

Failure mode we saw: model output rendered directly as HTML/markdown with no sanitization, so an injected link or script in the output becomes a live XSS vector; or model output passed to another system expecting a strict schema, with no validation, so a malformed or adversarially-shaped output propagates.

Implementation notes: sanitize anything rendered to a user; validate anything passed to another system against an expected schema and reject rather than best-effort-parse on mismatch. Also check what the model can see in its context in the first place; if secrets or other users' data are in context "just in case," they're one clever prompt away from leaking through the output.

## Layer 5: Red-teaming

Once layers 1-4 are in place, test them against the five attack categories: direct override, roleplay/jailbreak, fake system tags, encoding tricks, and indirect/RAG injection. See the paired `red-team-test-templates.md` in this kit for ready-to-run test cases in each category.

Failure mode we saw: teams treat a single red-team pass as a one-time gate before launch, then never re-run it after changing the system prompt, swapping models, or adding a new content source, all of which can silently reopen a closed hole.

Implementation notes: record actual transcripts for each test, not just pass/fail; re-run the suite on every system-prompt change, model change, or new content source; treat any fail as blocking unless the residual risk is explicitly accepted in writing by someone other than the feature's author.

---

See the paired `ship-safe-checklist.md` in this kit for a condensed pre-launch checklist version of these five layers, and `eu-ai-act-annex-iv-checklist.md` if you need to document these decisions for EU AI Act technical-documentation purposes.

© Ventrova. Guidance and reference material based on our own red-teaming research; not a substitute for a professional security audit of your specific system.
