Security

How to Map OWASP LLM Risks to Concrete Controls in Your Stack

Antonio Evans10 min read

A clean, diagrammatic illustration on a white background: on the left, a vertical list of ten labeled boxes (the OWASP LLM risks, drawn as abstract numbered squares with short labels such as Prompt Injection.
Key points

Key points

  1. 1Inventory every LLM call site and every tool, data source and credential the model can reach before you pick a single control.
  2. 2Map each OWASP LLM risk to one enforceable layer: input handling, output filtering or access scoping, never to a system prompt instruction alone.
  3. 3Give every control an owner, a measurable test and a pass threshold so the mapping becomes an audit artifact rather than a wiki page.
  4. 4Assume both the 2025 and 2026 OWASP numberings will circulate, and pin your mapping to the edition you actually assessed against.

Start with an inventory, not with a control catalog

The instinct after reading the OWASP list is to write controls. The more useful first move is to write down what exists. A control you cannot attach to a specific call site, credential or data path is a policy statement, and policy statements do not fail closed.

Two documents do most of the work. The first is a call-site register: one row per place your code invokes a model, whether that is a chat endpoint, a batch summarizer, a coding assistant, a retrieval step, an embedding job or an agent loop. For each row record the entry point, the trust level of the input (anonymous internet user, authenticated customer, internal staff, machine-to-machine), whether untrusted content such as web pages, uploaded files or tool output enters the context, and the output destination (a human, a database, a downstream API, a shell).

The second is a capability register: every tool, function, plugin or connector the model can invoke, every data store it can read, and the identity each one uses. OWASP's entry on excessive agency names exactly these three axes as the root cause, excessive functionality, excessive permissions and excessive autonomy (OWASP LLM06). Its examples are worth internalizing because they are ordinary engineering shortcuts, not exotic attacks: a document-reader extension that also carries delete rights, a plugin trialed in development that nobody removed, a retrieval tool that connects to the document repository as a privileged account seeing every user's files. If your register cannot answer "which OS identity does this tool use" for each row, you have found your first finding.

Do this with the people who wrote the code, in one sitting, on a shared sheet. Aim for completion, not precision. A register built in two hours and refined weekly beats a perfect one that never ships.

The five-step loop that turns a risk into a control

OWASP publishes a list of risks, not an implementation plan. The 2025 series gives each risk a definition, examples and prevention strategies (OWASP Top 10 for LLM Applications), and the project overall has grown into the broader GenAI Security Project with a dedicated 2026 edition that maps risks to NIST, MITRE ATLAS, CWE and the OWASP Top 10 for Agentic Applications (OWASP GenAI LLM Top 10 2026, OWASP project page). None of that tells you where in your stack to put the check.

The loop below does. Run it once per risk, per call site where the risk applies.

Step 1: pick one risk and one row from the register. "LLM01 prompt injection" across the whole product is not a work item. "LLM01 against the support chatbot that ingests user-uploaded PDFs" is.

Step 2: choose the enforcement layer. Three layers matter: input handling (constrain what reaches the model), output filtering (constrain what leaves it) and access scoping (constrain what the model can do even when it is fully compromised). The rule of thumb: if the harm happens when text enters the context, the control belongs at input. If the harm happens when text leaves, it belongs at output. If the harm happens because a tool executes, it belongs in permissions, and no amount of prompt wording substitutes. Practitioner testing of guardrails across client deployments found regex input filters catch roughly 60 to 70 percent of injection attempts, LLM-based classifiers catch 89 to 94 percent, and combining both with output validation reaches about 99.1 percent, while system prompt instructions alone fail against deliberate adversarial input (guardrail measurements). Those numbers come from one vendor's four projects, so treat them as a shape, not a benchmark. The shape is what matters: layered controls, none perfect, each covering the previous one's misses.

Step 3: write the control as an assertion a machine can check. "Validate inputs" is not a control. "Reject any request body over 32 KB before tokenization; log the rejection with request ID" is.

Step 4: name an owner and a test. The owner is a person, not a team. The test is a script, a red-team case, a log query or a policy check in CI, and it has a pass threshold.

Step 5: record it on the one-page map. Risk, call site, layer, control, owner, test, status. Ten risks by however many call sites you have. If the map exceeds a page per product surface, you are documenting prose again.

Repeat until each risk has at least one control on the surface where it is enforceable. Some risks need two or three. That is expected.

Input handling: what you can enforce before the model sees anything

Input handling is the cheapest layer and the one teams over-trust, because it feels like the familiar web-application boundary. Use it for what it genuinely stops.

Length and shape caps. Reject or truncate oversized inputs before tokenization. OWASP lists variable-length input floods and continuous input overflow as unbounded-consumption examples, and its first mitigation is strict input validation against reasonable size limits (OWASP LLM10). This is also a cost control: a 2 MB prompt costs money before it costs availability.

Encoding and structure checks. If an endpoint expects JSON, validate the schema and reject anything else. Markdown, HTML and base64 in a field that should hold a question are signals worth logging even when you let them through.

Source labeling. For indirect injection, the practical input control is provenance. Wrap retrieved web content, file extracts and tool output in delimiters that name the source, and strip invisible or formatting-hidden text at ingestion. OWASP's vector weakness entry documents exactly this attack, a resume with white-on-white instructions that says to recommend the candidate, passing through extraction into the knowledge base and being followed at query time (OWASP LLM08). The fix is extraction that ignores formatting and detection of hidden content before ingestion.

Allowlists, not blocklists, where the domain permits. A field that must contain one of twelve ticket categories should reject a novel value, not sanitize it. Blocklists of injection phrases are the weakest form: one practitioner writeup shows regex patterns like "ignore previous instructions" and "pretend you" catching low-effort attempts while the remainder use indirect phrasing, multi-turn buildup or encoding tricks (pattern examples).

What input handling cannot do is stop a determined attacker. A prompt-injection attempt can be a perfectly ordinary sentence. Treat input controls as signal collection and resource protection, and place the actual harm prevention downstream.

The most common mistake here is applying the same filtering philosophy to inputs and outputs. Aggressive input filtering can strip the domain terms, code or quoted material the model needs to answer correctly, while some failures only appear after generation (input versus output filtering). Match the control to the trust boundary it protects.

Output filtering: treat every response as untrusted text

Output filtering is where most of the value sits for disclosure, improper handling and misinformation risks, and it is where false positives bite hardest. The same practitioner measurements note that the worst production failures came from output guardrails that were too aggressive, not too permissive, because blocking legitimate queries destroys trust faster than an occasional bad answer (guardrail measurements). Design for a measured false-positive rate and a review queue, not a hard block on everything uncertain.

Four output controls cover most needs:

Schema validation. If the model must return JSON for a downstream service, validate against a strict schema, reject unknown fields and retry once, then fail. This is the direct control for improper output handling, where unchecked model output reaches another system and causes cross-site scripting, request forgery or command execution (OWASP risk overview). Cloudflare's guidance for the same risk is simply to treat the model like any untrusted user or device under a zero trust model, and validate output before it drives anything.

Encoding for the destination. Model output rendered into HTML gets encoded, output passed to SQL goes through parameterized queries, output passed to a shell never goes to a shell.

PII redaction and disclosure checks. Run a detector for account numbers, identifiers, credentials and personal data on the response before it leaves. This backs up LLM02, sensitive information disclosure, which covers PII, financial details, health records, confidential business data and credentials appearing in output (OWASP LLM02). Note OWASP's own caveat that system prompt restrictions on returning sensitive data may not be honored and can be bypassed, which is why the check has to be code.

Groundedness and action gating. For answers that drive decisions, require citations to retrieved sources and refuse when none exist. For anything that triggers a transaction, require human confirmation. Misinformation is a risk because credible-looking wrong output gets acted on, so the output layer is also where you gate the action.

A worked example makes the tradeoff concrete. Suppose your disclosure detector flags candidates on name plus account-number patterns. On a support corpus where 3 percent of legitimate answers quote a masked account reference, a naive block rule will reject 3 in 100 good answers. Tune the detector to require full, unmasked number formats plus a second identifier, and route the residual to a review queue with a 2 percent threshold before escalation. You get almost all the risk reduction without strangling the product.

Output filters need the same governance as any security control: versioned, with logged trigger rates, reviewed when the rate moves. The NHI guidance on validation and filtering urges teams to standardize where validation happens and monitor how often filters fire, because guard logic spread across ad hoc code paths drifts and becomes un-auditable (filtering governance).

Access scoping: the layer that survives a fully compromised model

Assume input and output controls fail on a bad day. Access scoping is what makes that day boring. It covers excessive agency, the parts of disclosure that come from over-broad reads, and vector cross-tenant leakage.

Per-tool credentials. Each tool gets its own identity with the minimum permissions its job needs. The document reader gets read on the user's documents, not read on the whole repository. OWASP's excessive agency entry lists the over-privileged-database-identity pattern first among its permission examples (OWASP LLM06).

Per-user data boundaries. Tools that act "for" a user must act as that user, not through a shared service account. This is the same entry's fifth example: an extension scoped to read the current user's document store that actually connects with a privileged account holding everyone's files. The fix is credential-per-user or, where that is impractical, mandatory row-level filters applied in the retrieval code, not the prompt.

Least-privilege API keys. Provider keys are often shared across teams, which erases individual accountability (LLM access control). Issue one key per person or service, cap spend per key, and rotate on schedule.

Task-scoped authorization. Research on a least-privilege framework for tool-calling agents proposes authorizing an agent through a role for the current task rather than a flat list of permanently granted tool permissions, and reports that this approach cut simulated permission confirmations by 43.4 to 89.4 percent versus per-tool prompting while mitigating privilege-escalation attacks (MiniScope paper). The paper is a design reference, not a product recommendation, but the decision rule is portable: grant the permissions the task needs, and for high-impact actions require explicit approval or a role that expires with the task.

Tenant partitioning in vector stores. Shared vector databases without strict logical partitioning leak embeddings across tenants, and embeddings are not inherently anonymized, so inversion can reconstruct source text (OWASP LLM08). A practical summary of the same risk lists permission-aware vector databases, dataset partitioning and classification tags as the mitigations (RAG security overview). Check for the query-time filter: if a retrieval query does not include the tenant or user identifier as a mandatory predicate, the control is missing regardless of what the schema says.

A checklist for the scoping review:

CheckPass conditionWhere it fails quietly
Tool credentialsEach tool has its own identity; read-only tools hold read-only grantsOne shared service account across all tools
Per-user scopeEvery user-facing tool carries the requesting user's identityService account with repository-wide access
High-impact actionsDelete, send, pay, deploy require confirmation or approvalTools that act on first model call
API keysOne key per person or service, with spend capsA team key pasted into a shared vault
Vector retrievalTenant or user filter enforced as a query predicateFilter applied only in the prompt
Retired toolsRemoved from the tool registry, not just unusedDev-phase plugins still registered
Logits and logprobsHidden from API consumersRaw probability output exposed

A one-page mapping that survives an audit

The map is the deliverable that turns ten risks into ten work items with names on them. Keep it to one page per product surface and one row per control.

Risk | Call site        | Layer     | Control (assertion)                   | Owner    | Test                        | Status
LLM01| chat.support     | output    | Refuse actions w/o user confirm        | A. Patel | case inj-07 blocks send      | pass
LLM02| report.summarize | output    | Redact acct numbers + 2nd identifier   | R. Okafor| sample set FPR < 2%          | open
LLM06| agent.inbox      | access    | Read-only mailbox token per user       | A. Patel | attempt delete returns 403   | pass
LLM08| rag.docs         | access    | Tenant filter mandatory in retrieval   | M. Chen  | cross-tenant query returns 0 | open
LLM10| all endpoints    | input     | 32 KB body cap, 429 over quota         | R. Okafor| load test: 0 5xx under flood | pass

Two conventions make the map durable. First, pin the edition: the 2025 list and the 2026 list use different numbers and, in the 2026 edition, prompt injection moves to a different position and hidden context exposure replaces system prompt leakage in the numbering, with new risks such as misinformation and unbounded consumption also reordered (comparison of editions). Record which edition you mapped against and when you will re-check. Second, keep the test column executable. A status of "pass" should mean someone can rerun the test.

Coverage rule: every risk in your chosen edition gets either a control row or a written, signed acceptance of the residual. Silence is not acceptance.

If the mapping exercise shows you have one team owning every layer, or that identity plumbing for tools does not exist yet, that is a good point to get an outside read on sequencing. A free 30-minute discovery call is a reasonable place to pressure-test your map against someone who has built the same layers.

FAQ

FAQ

Do I need one control per OWASP risk, or one per call site?

One per call site where the risk applies. A single product often has five call sites with different trust levels, and a control that fits the internal summarizer may be wrong for the public chatbot. Map risks to specific call sites from your register, not to the product as a whole.

Is a strong system prompt a valid control?

No. OWASP itself warns that system prompt restrictions may not be honored and can be bypassed (OWASP LLM02), and practitioner testing finds prompt instructions fail against deliberate adversarial input. Use prompts to shape normal behavior, and put enforceable checks in code at the input, output or access layer.

Where does prompt injection actually get stopped?

Mostly after the model, not before it. Input filters catch a large share of attempts but not all, so the real containment is output validation plus least-privilege tool permissions, so an injected instruction that survives has nothing dangerous to reach.

How do I handle the 2025 versus 2026 OWASP numbering?

Pick the edition you assessed against, state it on the mapping page, and note when you will re-check. Both numberings circulate, and the 2026 edition reorders risks and renames some entries (edition comparison). Consistency across your own documents matters more than which edition you choose.

What is the fastest way to find missing access scoping?

Ask one question per tool: if an attacker fully controlled the model output right now, what could this tool do? If the answer is anything beyond the task's needs, or uses an identity broader than the requesting user, you have a finding and a clear fix.

Sources

Sources

  1. LLM06:2025 Excessive Agency - OWASP Gen AI Security Project
  2. LLMRisks Archive - OWASP Gen AI Security Project
  3. OWASP GenAI LLM Top 10 2026
  4. OWASP Top 10 for Large Language Model Applications
  5. LLM Guardrails That Actually Work in Production
  6. OWASP LLM10: 2025 Unbounded Consumption
  7. LLM08:2025 Vector and Embedding Weaknesses
  8. What do teams get wrong about LLM input validation and ...
  9. What are the OWASP Top 10 risks for LLMs? - Cloudflare
  10. LLM02:2025 Sensitive Information Disclosure
  11. Understanding LLM Access Control - Maxim AI
  12. PDF A Least Privilege Framework for Authorizing Tool Calling Agents
  13. RAG Security: Vector & Embedding Weaknesses | OWASP LLM08
  14. OWASP Top 10 for LLM Applications: 2026 and 2025 - Superwise.ai
About the author

About the author

Antonio Evans
Antonio Evans

Antonio Evans is the founder of Quanta Intelligence and a fractional CTO who builds HIPAA-ready AI systems for health and life-science companies. Before that he was technical co-founder of FriendsEAT for twenty years, CEO of Socialdraft, and a data engineer at IBM. Antonio lives with his dog Santino in Lisbon, Portugal.

All articles