Teams adding guardrails to an LLM application usually ask the wrong question, which filter to buy, instead of the right one: which threats must be stopped before the model sees the text, and which can only be caught after it has written something. Input filtering and output filtering look similar on a vendor diagram, but they see different data, fail in different ways and carry different latency costs, especially once responses are streamed. This guide compares the two placements threat by threat, covers streaming, and sets out a layered design with a worked example. It is for AI platform engineers, AppSec teams and architects; guardrail products and detection techniques are covered in the AI Guardrails and AI Firewalls Guide.
Key takeaways
- Input filters reduce what reaches the model; output filters reduce what leaves it. Prompt injection and jailbreak attempts are best screened on the way in, while data leakage, system prompt disclosure and unsafe markup can only be judged on the way out.
- Neither is a security boundary on its own. OWASP and the UK NCSC both treat prompt injection as a residual risk, so the consequential controls (authorisation, egress allowlists, output encoding) must be deterministic and sit outside the model.
- Streaming weakens output filtering. Buffered modes add delay to every chunk; asynchronous modes remove the delay but let some text reach the user before a verdict, and the major cloud services document both behaviours.
- Retrieved and tool content is input too. Indirect injection arrives in documents, emails and tool responses, so an input filter that only scans the user's message misses the most damaging route.
- Place each control where it has the context to decide. Cheap deterministic checks run everywhere; classifiers run where the risk is; anything that authorises an action runs at the tool boundary with the acting identity.
The short answer
Use input filtering to cut the volume of attacks and abuse that reach the model, and to keep sensitive data you do not want sent to a third-party model out of the prompt. Use output filtering to stop the consequences you cannot predict from the input: personal data or secrets in the answer, a leaked system prompt, markup or URLs that turn into exfiltration, and content that breaks policy even though the request looked harmless. Most production systems need both, but not the same classifier twice. The design question is which layer owns which risk, and what the system does when that layer misses.
OWASP's LLM01:2025 entry lists input and output filtering as a mitigation, while its Prompt Injection Prevention Cheat Sheet says input screening is useful for logging, review and rate limiting but is not a security boundary, because the model does not separate instructions from data. The NCSC's December 2025 blog "Prompt injection is not SQL injection (it may be worse)" warns against relying on deny-listing known bad content. Filtering at either end buys risk reduction, not prevention.
Input filtering vs output filtering at a glance
| Dimension | Input filtering | Output filtering |
|---|---|---|
| What it inspects | User message, conversation history, retrieved chunks, tool responses, uploaded files, before inference | Model completion, structured output, tool call arguments, citations and links, after inference |
| Primary threats | Direct and indirect prompt injection, jailbreak attempts, sensitive data sent to the model, oversized or malformed input, abuse and cost attacks | Sensitive information disclosure, system prompt leakage, harmful or off-policy content, unsafe markup and URLs, ungrounded answers |
| What it cannot know | Whether the model will actually comply, what data the model will pull from context, whether a benign prompt yields a harmful answer | The intent behind the request, whether the answer was produced under attacker influence, anything outside the text it is shown |
| Latency cost | Added before the first token unless run in parallel with generation | Added to every chunk (buffered) or to the verdict only (asynchronous) |
| Cost when it fires | Cheap: the model call can be skipped | Expensive: the generation has already been paid for |
Input filtering in depth
Threat model
Input filtering assumes the attacker controls some of the text that will enter the context window. That is the user in a chat application, but in retrieval and agent systems it is also anyone who can write to a document, mailbox, web page, ticket or API the system reads. OWASP LLM01 distinguishes direct injection through the prompt from indirect injection through external content, and the second is where input filters most often have a gap: they are wired to the chat box and never see the retrieved text. Input filtering also addresses a different risk entirely: data you do not want to send to a model provider at all, such as card numbers or health records pasted by staff.
Architecture in words
Deterministic checks run first (size, encoding, rate), then a prompt attack classifier scores the untrusted parts, then PII detection masks or blocks regulated data, and only then does the call go to the model. Where latency matters, the classifier call is sent at the same moment as the model call and the answer is withheld if the classifier fires; the OpenAI Cookbook guardrails example describes this parallel pattern.
Scope the scan to the untrusted parts. Amazon Bedrock Guardrails input tagging lets an application wrap only selected content in a reserved tag so that the rest of the prompt is not evaluated, and its documentation notes that the prompt attack filter requires those tags. AWS recommends a new random tag suffix on every request so that an attacker cannot close the tag and append content. AWS's RAG example leaves search results untagged as trusted; tag them if outsiders can edit the sources. Azure AI Content Safety Prompt Shields makes the same split explicitly: user prompt attacks are scanned in the userPrompt field and document attacks (instructions hidden in documents, emails and web pages) in a separate documents field, with document attack detection off by default in Foundry.
Failure modes
- Encoding and language evasion: OWASP LLM01 scenario 9 describes instructions in other languages or in Base64 and emoji. The OWASP cheat sheet recommends normalising text before screening, flagging unexpected zero-width and Unicode tag characters (U+E0000 to U+E007F), and decoding Base64 or hex candidates for inspection only.
- Context window truncation: small classifiers have short windows. Meta's Llama Prompt Guard 2 models support 512 tokens, and the model card advises splitting longer prompts into segments and scanning them in parallel. A filter that silently truncates a long document scans only its first page.
- Intent the classifier was not trained on: Prompt Guard 2 labels a prompt malicious when it explicitly tries to override prior instructions, and its card warns that some attacks are application-dependent. An instruction that reads as normal business guidance passes; EchoLeak bypassed Microsoft's cross-prompt injection classifiers this way. Microsoft also states that Prompt Shields models are primarily trained and evaluated on English, and notes that a clean result does not establish that a document is trustworthy.
Controls that make input filtering worth having
- Drop privileges when untrusted content is present: the NCSC blog cites the principle that when a model processes a party's data, its privileges should fall to that party's. An input filter can set that flag even when it cannot block: a request carrying external email gets no write tools.
- Mask regulated data before external calls: mask or tokenise card numbers, national identifiers and secrets before the prompt leaves your boundary, and keep the unmasking key out of the model's reach. See the GenAI DLP vs Traditional DLP.
Output filtering in depth
Threat model
Output filtering assumes the model will sometimes produce something it should not, whether because an attack succeeded, because the context held data the user should not see, or because the model simply erred. OWASP LLM05:2025 Improper Output Handling treats the output as untrusted for any downstream component; LLM02 Sensitive Information Disclosure lists a user receiving another user's personal data as a scenario; LLM07 System Prompt Leakage recommends an independent system outside the model that inspects output for compliance, rather than relying on system prompt instructions. Only the output layer sees what the model actually did.
Architecture in words
The completion passes through, in order: schema validation where the application expects structured data; deterministic scans for secrets, internal hostnames and system prompt fragments; PII and entity detection with block or mask actions; a harm or policy classifier; and finally sink-specific handling (HTML encoding, Markdown sanitisation, URL allowlisting) at the point where the text is rendered or passed on. Tool call arguments are output too: they are validated against a schema and checked against policy before the tool runs. The sink-by-sink controls are in the OWASP LLM Top 10 LLM05 Improper Output Handling Guide.
Failure modes
- The filter sees text, the renderer sees markup: a filter that strips inline links but not reference-style Markdown images lets data leave in an image URL. That is the route EchoLeak used; see the EchoLeak 2025 analysis. Sanitise at the renderer, not only in a text classifier.
- Streaming tail: Microsoft's documentation records that the last segment of a streamed response may be returned before the guardrail policy is applied to it, and gives a test in which a blocklisted term in roughly the last 15 to 30 words reached the client.
Controls that make output filtering worth having
- Deterministic first: schemas, length caps, secret patterns and URL allowlists rarely misfire and do not depend on a model judging another model.
- Grounding checks for factual products: compare claims against retrieved sources where wrong answers carry risk; OWASP LLM01 names the RAG Triad (context relevance, groundedness, answer relevance) for spotting manipulated outputs.
Which placement stops which threat
| Threat | Input filter role | Output filter role | Deterministic control that holds when both miss |
|---|---|---|---|
| Direct prompt injection and jailbreak (OWASP LLM01; ATLAS AML.T0051, AML.T0054) | Primary: classifier on the user turn, rate limiting of repeat attempts | Secondary: catches the harmful result if the attempt worked | Tool authorisation outside the model; least privilege on the agent identity |
| Indirect injection in documents and tool results | Primary: document attack scan on retrieved and tool content, provenance tagging | Secondary: detects exfiltration markup, unexpected URLs | Privilege drop when untrusted content is present; egress allowlist |
| Sensitive data sent to an external model | Primary: PII and secret masking before the call | None (the data has already left) | Data classification, approved model list, contractual retention terms |
| Sensitive information disclosure in answers (LLM02) | Weak: cannot predict what the model will retrieve | Primary: PII and secret detection, block or mask | Permission-filtered retrieval as the caller |
| System prompt leakage (LLM07) | Weak: extraction requests are easily rephrased | Primary: matching of system prompt fragments | No secrets or permission logic in the system prompt at all |
| Unsafe markup, code or URLs (LLM05) | None useful | Primary: schema validation, sanitisation, allowlists | Context-aware encoding at the sink, CSP, sandboxed execution |
| Harmful or off-policy content | Useful for clearly abusive requests | Primary: response classifier, since benign prompts can yield bad answers | Human review for high-impact publication |
| Cost and denial of service (LLM10) | Primary: size limits, quotas per identity | Output token caps | Per-identity budgets in the gateway |
Latency and streaming: what each placement costs
Input checks add time before the first token. A small classifier is cheap: Meta's model card reports 92.4 ms per classification for Prompt Guard 2 86M and 19.3 ms for the 22M model at 512 tokens on an A100 GPU. LLM judges cost a full extra model call. Running it in parallel with generation hides that cost but pays for discarded generations.
Output checks are harder because users expect token-by-token streaming. Both large cloud providers document the same trade-off with different names:
| Mode | Behaviour as documented | What reaches the user | Use when |
|---|---|---|---|
| Buffered or synchronous (Azure OpenAI default streaming; Amazon Bedrock Guardrails synchronous mode, the default) | Chunks are buffered and evaluated before release; Bedrock says this adds latency but every chunk is scanned first | Text arrives in bursts; Azure notes the final segment may still be returned before policy is applied | Regulated content, customer-facing answers, anything sent onward automatically |
| Asynchronous (Azure Asynchronous Filter, configured per deployment; Bedrock streamProcessingMode ASYNCHRONOUS) | Tokens stream immediately while filters run in the background; Azure guarantees the signal within about 1,000 characters of the violating content, then stops the stream | Some violating text can be displayed before the signal; Bedrock does not support sensitive information masking in this mode | Internal assistants where low latency matters and the client can retract displayed text |
| No streaming | Full response checked before release | Nothing until the check completes | Agent steps, tool arguments, emails, code to be executed |
Asynchronous filtering only works if the client acts on late signals. Azure returns annotations with offsets during the stream and tells applications to stop displaying content and remove or replace text already shown when a signal arrives. If your front end cannot retract text, you are not running asynchronous filtering, you are running logging.
Layering both: a worked scenario
A retailer runs a customer service assistant that reads order history and the customer's emails to support, and can issue refunds up to a set value. Consider an attacker who sends a support email containing hidden instructions to list other customers' orders and render them inside an image link.
- Input, deterministic: the email body is length-limited, normalised and stripped of Unicode tag characters.
- Input, classifier: the email is sent to a document attack scan, separately from the customer's own message. Assume it is phrased as ordinary guidance and passes, as in EchoLeak.
- Input, privilege decision: because external email is in context, the session loses the refund tool for that turn, whatever the classifier said.
- Retrieval: order lookups run as the authenticated customer, so other customers' orders are never in context. This is what actually prevents the disclosure.
- Output, deterministic: the response is rendered with Markdown images disabled and links limited to an allowlist of the retailer's domains, so even a successful injection has no exfiltration channel.
- Output, classifier: PII detection on the response blocks any payment data; the stream is buffered for this customer-facing flow.
- Monitoring: the input flag, removed tool and any output block are logged against the session.
The two classifiers are the least reliable layers here; they generate signals and cut noise, but retrieval permissions, tool authorisation and rendering rules decide the outcome.
Decision guidance: when to choose which
- Prefer input filtering when the main risk is abuse of a public endpoint, cost, data you must not send to a provider, or injection through content you ingest; and when blocking early saves expensive generations.
- Prefer output filtering when the main risk is what the model might reveal or produce: regulated data in context, system prompts with business logic, generated markup or code, published content.
- Run both for retrieval and agent systems that read external content and hold data or tools of value. Give each layer a different job rather than the same classifier at both ends.
- Move to the tool boundary as soon as the model can act. Output filtering of a tool call is necessary but not enough; the call must be authorised against the agent's identity and the delegated user. See Prompt Guardrails vs Policy Engines for AI Agent Actions.
- Small organisations: start with the provider's built-in input and output filters, buffered streaming for customer-facing flows, output schemas and a link allowlist. That covers most risk with little engineering.
- Large organisations: centralise input policy, quotas and logging in a gateway and keep output policy close to each application, where the data classification and sinks are known. See AI Guardrails vs AI Gateways.
Rolling out both layers
- Map threats to layers: fill in the threat table above per feature and name the deterministic backstop for each row.
- Deploy deterministic checks: size limits, normalisation, output schemas and link allowlists, enforced from day one.
- Run classifiers in annotate mode: both layers log verdicts on live traffic for two to four weeks; label samples to set thresholds per category and language.
- Enforce in rings: block for internal users, then a pilot group, then everyone, with buffered streaming for customer-facing flows.
- Keep a rollback: a per-policy switch back to annotate mode, and rerun the attack suite after every model, prompt or policy change.
Detection and measurement
Log the verdict, score, policy version and action for every check at both ends, with the session and caller identity, and redact the logged text with the same rules as the output filter. See the LLM Observability Guide.
- Input miss rate: share of red team and incident-derived attack prompts that pass the input filter, per language and per source (user turn vs document).
- Output catch after input pass: output blocks on requests the input filter allowed; a rising figure means the input layer is drifting.
- False positive rate per layer: sampled and labelled weekly from real traffic, tracked separately for input and output.
- Added latency: p95 time to first token and p95 inter-chunk delay attributable to filtering.
- Late signal exposure: in asynchronous mode, characters displayed before a violation signal, and the share of retracted responses.
Framework mapping
| Framework | Input filtering | Output filtering |
|---|---|---|
| NIST SP 800-53 Rev 5 | SI-10 Information Input Validation; AC-4 Information Flow Enforcement for data sent to external models | SI-15 Information Output Filtering; AC-4 for egress of generated content |
| NIST CSF 2.0 | PR.DS-02 data in transit to the model provider; DE.CM-09 monitoring of runtime inputs | PR.DS-02 data leaving in responses; DE.CM-09 monitoring of outputs |
| MITRE ATLAS mitigations | AML.M0020 Generative AI Guardrails; AML.M0033 Input and Output Validation for AI Agent Components | AML.M0020 (ATLAS notes guardrails can detect PII and harmful code in outputs); AML.M0033 |
| OWASP Top 10 for LLM Applications 2025 | LLM01 Prompt Injection, LLM10 Unbounded Consumption | LLM02 Sensitive Information Disclosure, LLM05 Improper Output Handling, LLM07 System Prompt Leakage |
Common failure patterns
- Scanning only the chat box: the input filter never sees retrieved documents or tool results, so indirect injection walks past it. ForcedLeak hid instructions in a public web-to-lead form field that the agent later read, then sent CRM data out through an image URL on an expired but still allowlisted domain; see the ForcedLeak 2025 analysis.
- Asynchronous mode without retraction: the team turns on asynchronous streaming for speed, but the front end ignores annotations, so violations are displayed and only logged.
- Output masking as the only privacy control: retrieval returns data the user should not see and a regex is expected to catch every format.
Practitioner checklist
- For each LLM feature, list which threats the input layer owns, which the output layer owns, and which deterministic control backs each.
- Scan retrieved documents, emails and tool responses as separately labelled untrusted input, not only the user's message.
- Limit length, normalise Unicode and strip tag characters before any classifier runs.
- Mask regulated data before calls to external model providers; keep unmasking keys away from the model.
- Choose buffered streaming for customer-facing and regulated flows; use asynchronous mode only where the client retracts flagged text.
- Never stream tool arguments or agent steps to their consumer; check each one in full before it is used.
- Sanitise Markdown and allowlist link and image hosts at the renderer.
- Check every output channel your guardrail evaluates, including reasoning, tool arguments and attachments, and close the gaps.
- Track miss rate, false positive rate and added latency separately for each layer, and retest after every model or policy change.
Standards and references
- OWASP: LLM01:2025 Prompt Injection
- OWASP: LLM07:2025 System Prompt Leakage
- OWASP: LLM Prompt Injection Prevention Cheat Sheet
- NCSC: Prompt injection is not SQL injection (it may be worse)
- Microsoft Learn: Content streaming in Azure OpenAI
- AWS: Configure streaming response behaviour to filter content in Amazon Bedrock Guardrails
- AWS: Apply tags to user input to filter content in Amazon Bedrock Guardrails
- Microsoft Learn: Prompt Shields in Azure AI Content Safety
Related NHI Mgmt Group resources: AI Guardrails and AI Firewalls Guide · Prompt Injection Guide · OWASP LLM Top 10 LLM02 Sensitive Information Disclosure Guide · LLM Security Guide