Skip to content
HACKTECH
Guide

Written for: Engineering leads and product security owners shipping chat assistants, retrieval search or AI agents

Securing LLM-powered features: prompt injection, agents and the OWASP Top 10 for LLM Applications

Prompt injection, agent risks and data leaks through rendered output, mapped to the OWASP Top 10 for LLM Applications (2026 update), with fixes and a test plan.

Published · Updated · 13 min read

With a language model in the product, text that used to be inert, such as a support ticket, a PDF a customer uploads or a web page your assistant summarises, can steer software that holds your users' data and, once it has tools, the power to act on it.

Why prompt injection is different from SQL injection

SQL injection has a structural fix. Parameterised queries keep the code channel and the data channel apart, so an apostrophe in a surname can never become a command. Large language models have no equivalent boundary: the system prompt, the user's message, a retrieved document and a tool's output all arrive as one sequence of tokens, and the model decides what to treat as an instruction. In December 2025 the UK's National Cyber Security Centre wrote that current models make no distinction between data and instructions, and that prompt injection attacks may never be totally mitigated in the way SQL injection was.

The NCSC suggests treating the model as an inherently confusable deputy, and that framing changes the engineering question. Filtering hostile input still helps, but the decisive question is what the system can do, and to whom, on the day the model is fooled. A feature whose worst case is an embarrassing reply has a content problem. A feature whose worst case is sending another customer's records to an attacker has a security architecture problem, and that has to be solved outside the model.

Classic injection and prompt injection compared
QuestionSQL or command injectionPrompt injection
Where is the boundary?Between code and data, enforced by the database driver or a safe APIThere is none inside the model: instructions and data share one input
Is there a complete fix?Yes: parameterised queries, safe APIs and output encodingNo known complete fix, so you reduce the likelihood and limit the impact
Does input filtering work?Allow-list validation is effectiveFilters and classifiers catch known phrasings and miss new ones
What does a test prove?That a specific parameter is or is not exploitableWhat the system allows once the model follows hostile text
Where does the control live?In how the query or command is builtIn permissions, confirmation steps and output handling around the model

Direct and indirect prompt injection

The OWASP Top 10 for LLM Applications 2025 lists prompt injection first, as LLM01:2025, and separates two forms. Direct injection happens when a user's own input alters the model's behaviour, such as the familiar instruction to ignore previous guidelines typed into a chat box. Indirect injection happens when the model accepts input from external sources, such as websites or files, and that content alters its behaviour. The idea was set out in a 2023 paper by Greshake and colleagues, who argued that applications which retrieve data blur the line between data and instructions, so an attacker can plant instructions in content the system is likely to fetch later.

Direct injection mostly threatens your own content policies. Indirect injection is the serious one for most products, because the attacker never needs an account: they only need to get text in front of the model. These are the paths we test:

  • Documents customers upload for summarising, extraction or question answering
  • Web pages fetched by a browsing or research tool
  • Emails, support tickets, chat transcripts and calendar invitations processed by an assistant
  • Entries in a retrieval knowledge base, including pages any employee can edit
  • Responses from tools and third-party APIs that the agent calls
  • Code, comments, README files and issue threads read by a coding agent
  • Text hidden from people but not from the model: white-on-white text, HTML comments, image alt text and metadata

A well-documented example is CVE-2025-32711, a critical flaw in Microsoft 365 Copilot published in June 2025, which researchers at Aim Labs called EchoLeak. The attacker sent an ordinary-looking business email containing hidden instructions, phrased as if written for a person so that Microsoft's injection classifier let it through. When the recipient later asked Copilot a related question, the retrieval engine pulled the email into the model's context, and the injected instructions made the model place internal data inside a link or image pointing at the attacker. Nobody had to click anything. Microsoft fixed it on the server side, reported no evidence of exploitation, and customers had nothing to install.

Agents, tools and the confused deputy

Once a model can call tools, injection stops being a content problem and becomes an access-control problem. A confused deputy is a program that holds authority and is tricked by a less privileged party into misusing it. An agent is a deputy by design: it holds the user's session, an API key or a service account, and it reads text written by people who hold none of those. OWASP calls the resulting risk Excessive Agency, LLM06:2025, and traces it to three root causes.

The three root causes of excessive agency, and the design response
Root causeWhat it looks like in a productDesign response
Excessive functionalityAn assistant that only needs to look up orders is given a general web fetch, a shell or a free-form database query toolOffer only the tools the task needs, each narrow and single-purpose, and avoid open-ended extensions
Excessive permissionsTools run as a service account that can read every tenant's data or write anywhereRun each action in the context of the requesting user, with the least privilege it needs
Excessive autonomyRefunds, emails, deletions and permission changes happen before anyone sees themRequire a person to approve high-impact actions before they run

OWASP adds a principle that matters more than any prompt: complete mediation. Authorisation belongs in the downstream systems, which check every request on its own merits, instead of in the model's judgement about whether an action is allowed. If your billing API would refuse a refund to an ordinary user, it must refuse the same refund when the agent asks on that user's behalf.

For agent designs, the most useful single test we know is Simon Willison's lethal trifecta. An agent that combines access to private data, exposure to untrusted content and the ability to communicate externally can be made to hand that data to an attacker, and the only reliable defence is to avoid the combination entirely. The NCSC cites a related principle: when a model processes information from a party, its privileges should drop to that party's. In practice, the agent that reads inbound email should not also hold the tools that query your customer database or send messages outside the company.

Data exfiltration through rendered output

The quietest exfiltration channel needs no tools at all. If the model can be persuaded to write a markdown image whose address contains your data, the user's browser requests that address automatically and delivers the data to the attacker's server. OWASP's prompt injection entry describes exactly this scenario. Microsoft's Security Response Center lists HTML and markdown images and clickable links among the exfiltration techniques demonstrated against LLM-based systems. After a researcher reported exfiltration through markdown image injection, Microsoft took steps to block that technique deterministically, and took similar steps against others, such as the generation of certain untrusted links. EchoLeak showed the second-order problem: a content security policy only helps if no allow-listed domain can be abused to carry the data out.

The wider weakness is Improper Output Handling, LLM05:2025: model output passed to other components without validation. OWASP's examples include output entered directly into a system shell or into exec or eval, JavaScript or markdown returned to a user's browser, SQL generated by the model and executed without parameterisation, file paths built from output, and email templates filled with unescaped text. The fix is old and unglamorous: treat the model like any other untrusted user.

  • Treat model output as untrusted text and encode it for the context it lands in: HTML, markdown, SQL, shell or file path.
  • Do not render images or preview links from arbitrary domains; strip or proxy them, and allow-list the few you need.
  • Keep a strict content security policy, and review every allow-listed domain for ways to carry data out.
  • Use parameterised queries for any database operation that involves model output, and never pass output to a shell, exec or eval.
  • Validate structured output against a schema before any downstream system acts on it.
  • Remove hidden content such as HTML comments from retrieved text before it reaches the model.

The OWASP Top 10 for LLM Applications: the 2025 list and the 2026 update

The OWASP GenAI Security Project published the 2025 edition on 18 November 2024, and this guide was first written against it. We now map findings to the OWASP Top 10 for LLM Applications 2026, described below, alongside MITRE ATLAS, because it gives your engineers, auditors and customers a shared vocabulary. Teams building autonomous agents should also read the OWASP Top 10 for Agentic Applications for 2026, published in December 2025.

The ten risks under their 2025 and 2026 numbers, and what we look for in your product
2025 entry2026 entryRiskWhat we look for
LLM01:2025LLM01:2026Prompt InjectionDirect and indirect instructions that change behaviour, through every path text takes to the model
LLM02:2025LLM02:2026Sensitive Information DisclosureOther users' or tenants' data, internal records and secrets appearing in responses
LLM03:2025LLM04:2026Supply ChainThird-party models, adapters, plugins and datasets with unknown provenance or licences
LLM04:2025LLM05:2026Data and Model PoisoningTraining, fine-tuning or embedding data that an outsider can influence
LLM05:2025LLM10:2026Improper Output HandlingModel output reaching browsers, shells, databases or APIs without validation
LLM06:2025LLM03:2026Excessive AgencyTools, permissions and autonomy beyond what each task needs
LLM07:2025LLM08:2026System Prompt Leakage (Hidden Context Exposure in 2026)Credentials, internal rules or security logic placed in the system prompt
LLM08:2025LLM09:2026Vector and Embedding WeaknessesRetrieval that ignores document permissions, and leakage between tenants in a shared vector store
LLM09:2025LLM07:2026MisinformationConfident, wrong answers that users or systems act on without a check
LLM10:2025LLM06:2026Unbounded ConsumptionMissing quotas, rate limits and cost caps, including agent loops that never end

How we test an LLM feature, in seven steps

An AI and LLM application test treats the model as one component of a system and attacks the system. We run it grey-box: we need the prompts, the tool definitions and the identities those tools run as, because that is where the impact lives.

  1. 1

    Map the feature

    We draw the data flows with your engineers: the model and its version, system prompts, retrieval sources, tools, the credentials behind each tool, and every place output is displayed or consumed. For a feature still being designed, this is a short threat model.

  2. 2

    Build an injection corpus for your content

    Direct prompts, plus indirect payloads planted in each ingestion path you have: uploaded files, web pages, emails, tickets and knowledge base entries, including hidden text and attacks spread over several turns.

  3. 3

    Attack the boundaries between users

    We try to retrieve other users' and other tenants' documents, extract the system prompt, and check whether retrieval respects the permissions of the person asking.

  4. 4

    Drive every tool

    For each tool we try to make the agent call it with parameters we choose, skip or spoof the confirmation step, and reach data or actions the user could not reach directly.

  5. 5

    Try every way out

    Markdown images, links, HTML, tool calls to external addresses and anything else that can carry data out of the conversation.

  6. 6

    Test the ordinary surface

    The model sits behind an ordinary API that needs authentication, authorisation, rate limits and cost controls, so we test it as we would any web application.

  7. 7

    Report, and hand over a regression set

    Every finding comes with an attack transcript you can replay against your deployment, mapped to the OWASP Top 10 for LLM Applications 2026 and MITRE ATLAS, plus regression prompts your team re-runs after every model or prompt change. The engagement includes a free retest of every fixed finding within 30 days of report delivery.

Most engagements take 4 to 10 working days, depending on how many tools and ingestion paths the feature has.

Design controls and a pre-release checklist

No single control is enough, so build layers that fail independently. In rough order of value:

  1. Least privilege for tools. Narrow, single-purpose tools, read-only by default, acting with the requesting user's credentials rather than a shared service account.
  2. Complete mediation. Every downstream system enforces its own authorisation on each request the agent makes.
  3. Human confirmation for consequential actions. Show the exact action and its parameters before anything is sent, paid, deleted or shared. Microsoft takes this route in Outlook: Copilot drafts the text, but the user must approve it and send the email themselves.
  4. Break the trifecta. Separate the component that reads untrusted content from the one that holds private data or an outbound channel.
  5. Allow-lists wherever you can. For the domains you render, the addresses tools may fetch, the tools an agent may call and the recipients it may contact.
  6. Output handling. Encode, parameterise and schema-check model output before anything acts on it.
  7. Mark untrusted content. Delimit or tag retrieved text so the model can tell it apart from instructions. Microsoft calls its version spotlighting and describes it as probabilistic, which is the right way to think about every technique in this category.
  8. Limits and logging. Rate limits, token and cost quotas, and logs of prompts, tool calls and outputs that your team can investigate.

Before release, check each of these against the build you are about to ship:

  • Every tool is listed with the identity it runs as and the worst action it can take.
  • High-impact actions need a person's approval, and the approval screen shows the real parameters.
  • Downstream APIs reject requests the user could not make directly, whoever sends them.
  • Retrieval filters documents by the asking user's permissions before ranking, in every tenant.
  • No credentials, connection strings or authorisation rules live in the system prompt.
  • Images and links from domains outside the allow-list are not rendered in model output.
  • Model output is encoded for the browser and never reaches a shell, eval or an unparameterised query.
  • Indirect injection has been tested through every ingestion path, including hidden text.
  • Quotas and rate limits stop runaway agent loops and cost abuse.
  • A regression prompt set runs before every model, prompt or tool change.

If you are about to ship a chat assistant, a copilot or an agent, start with the pentest scoping call. A 45-minute scoping call ends with a fixed quote and a proposed start date; the written scope follows by email. If the feature is still on a whiteboard, a threat modelling workshop is the faster route and changes the design while it is still easy to change. Where the model sits inside a larger product, an application security audit reads the code paths that decide what the agent is allowed to do.

Sources

Services that put this into practice

  • Adversarial testing of features built on large language models: chat assistants, retrieval-augmented search, agents with tool access, and generated output that reaches users or other systems. We test whether the model can be steered into leaking data, taking actions it should not, or passing untrusted output into the rest of your stack.

    Duration: 4 to 10 working days

  • Facilitated workshops that map how a system or feature could be attacked before it is built or shipped. We draw the data flows with your engineers, walk them with STRIDE, rank the threats by likelihood and impact, and turn the result into backlog items and test cases your team owns.

    Duration: 2 to 5 days per system, remote or on site

  • Authenticated, role-aware testing of your application and its API surface, with an account for every role. We chain small findings into the access they lead to, such as a sequential invoice ID and a missing tenant check that together let one customer read another's invoices.

    Duration: 5 to 10 working days

  • A white-box review of your application: source code, architecture, dependencies and build pipeline. Automated analysis finds the candidates; reviewers confirm each one by reading the code path, and only confirmed defects reach the report, each with a file and line reference.

    Duration: 5 to 15 working days, depending on codebase size

Read next

Scope the work in one call

Tell us what you ship and who is asking for assurance. A 45-minute scoping call ends with a fixed quote and a proposed start date; the written scope follows by email.