Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

🏠 Back to Blog

Stored (Persistent) XSS

Why this type first

Before hunting or exploiting XSS, it helps to know which flavor you are dealing with—stored, reflected, or DOM-based—because discovery, impact, and delivery differ.

What stored XSS is

Stored XSS (also persistent XSS) means the malicious payload is saved (typically in a database or similar) and embedded in the page whenever it is loaded. Anyone who visits that page can execute the payload, so the blast radius is large.

That combination—wide audience and durability—usually makes stored XSS the most critical XSS variant. Cleanup may require removing or correcting data in the back end, not only patching code, because the bad string lives in stored content.

Example pattern (lab-style app)

A minimal pattern (e.g. a to-do list that echoes new items into the DOM): you add normal text and it appears on the page. If the app does not sanitize or encode that input before rendering, it may accept HTML/script and be XSS-prone.

Testing payloads

A common proof-of-concept is obvious script execution:

<script>alert(window.origin)</script>

If input is reflected into HTML without encoding, the browser runs the script. alert(window.origin) is useful because it shows which origin executed the code—handy when cross-domain iframes isolate forms: the alert tells you which frame or app is actually vulnerable, not just “something popped.”

View source (Ctrl+U / “View Page Source”) should show the payload in the delivered HTML (e.g. inside the list markup), confirming the server (or template) placed it there.

Alternatives when alert() is restricted

Some environments or browser policies interfere with alert(). Other simple probes:

PayloadEffect
<plaintext>Stops HTML parsing after it and shows the rest of the document as raw text—very visible if it fires.
<script>print()</script>Opens the print dialog—often still allowed where alert is blocked.

Use the app’s reset (or equivalent) between tests if you need a clean state.

Confirming persistence

Refresh the page after submitting the payload. If the alert (or other effect) returns on every load, the string is stored and re-served—stored/persistent XSS. Any other user hitting the same page gets the same behavior, not only the attacker’s session.