How it works
The explanation, with examples you can run.
Prompt injection
Direct injection is a user typing instructions meant to override yours ("ignore your rules and..."). Indirect injection hides instructions in content the model reads on the user's behalf: a web page, an email, a PDF, a code comment, a support ticket, a tool result. Greshake et al. showed in 2023 that this can turn an assistant against its own user, for example by making it leak data or push a phishing link.
There is no known complete defence, so the goal is to limit what a successful injection can achieve:
- Least privilege. Give the model only the tools and data the task needs. A summariser does not need an email-sending tool.
- Enforce permissions in code, using the signed-in user's identity, never an identity or scope the model chooses.
- Separate untrusted content. Mark retrieved and user-supplied text clearly as data, and tell the model not to follow instructions inside it. This helps, but treat it as a speed bump, not a wall.
- Confirm consequential actions with a human: sending, paying, deleting, changing permissions.
- Watch for the dangerous combination: access to private data, exposure to untrusted content, and a way to send data out (tools, links, images). If a feature has all three, an injection can exfiltrate data; remove one of them where you can.
Sensitive information
- Assume anything in the context can be repeated back, including the system prompt. Do not put API keys, internal URLs or other customers' data in prompts.
- Retrieve only what the current user is allowed to see, and filter at query time, before content reaches the model.
- Check your provider's data retention and training terms, and whether you need a regional or zero-retention option for regulated data.
- Logs of prompts and outputs contain user data. Apply the same access control, retention and redaction rules as for the rest of your production data.
Output handling
Model output is untrusted input to whatever comes next. If you render it as HTML or Markdown, it can carry scripts or tracking images. If you pass it to a shell, a SQL query, a template engine or eval, it can run code. If you use it as a URL, it can point anywhere.
The example sanitises a reply before rendering: it escapes HTML and only keeps links and images that point to an allowlisted domain. The reply simulates a successful injection that tries to exfiltrate data through an image URL.
Press Run to see what your code prints, in order.
Runs in this browser, in an isolated frame with no access to this page. console.log, info, warn and error are shown; code runs as a classic script, so use an async function for await.
In production, use a maintained sanitiser (such as DOMPurify in the browser) and a Content Security Policy that restricts where images and scripts can load from, rather than hand-written regular expressions.
Excessive agency and runaway use
- Grant tools the narrowest scope: read-only where possible, specific resources rather than "any", and per-user credentials held by your code.
- Rate-limit and budget per user. OWASP calls the cost side "unbounded consumption": long inputs, huge outputs and loops can be used to run up your bill or exhaust your quota.
- Log tool calls and review them; alert on unusual patterns.
Harmful or wrong output
Safety also covers what you show users. Decide what the feature must refuse or redirect (medical, legal, financial advice, self-harm, topics outside your product), test those cases in your evaluation set, and consider a moderation or classification step on inputs and outputs. For factual answers, ground them in sources and show citations, so users can check.
Check yourself
3 questions. Get them all right to finish this topic.
0 of 3 right so far. Get all three to finish this topic.