Stored XSS
Planted once, it runs for every visitor after that.
What Is Stored XSS?
Stored cross-site scripting only has to be planted once. Every user who loads the page afterwards runs the attacker's script against their own session, with no link to click and nothing to notice. You'll drop an image tag with an onerror handler into a guestbook, then load the page yourself and watch your session cookie print. The fix escapes at the rendering boundary, so the identical payload still sitting in the database renders as text. The stored value never has to be cleaned up for the fix to hold.
What You'll Learn in Stored XSS
- Distinguish Stored XSS from Reflected XSS, the durable persistence of the payload on the server is what changes the threat model
- Explain why an <img src=x onerror=alert(...)> tag fires even when there is no <script> tag in the payload
- Identify the rendering-boundary HTML escape as the correct primary defense, and explain why input-layer blocklists and CSP-only strategies fall short
- Read a Node.js render function well enough to spot a concat-into-HTML bug and recognize the line where the escape needs to be inserted
- Understand that the stored value can stay exactly as-is once the renderer escapes properly, the database does not need to be sanitized for the fix to work
Stored XSS — Training Steps
-
Find the community forum
Today Bob is targeting Signal Coast, whose community forum lets any member post a short public note that every other member reads. He signs in with a throwaway account and opens the forum's guestbook to see how it handles what people write.
-
Notes render as written
The guestbook makes a promise most sites are careful never to make: it renders each note exactly as written, HTML included. A field that turns whatever you type into live markup is exactly what Bob was hoping to find.
-
Post a normal note
First, Bob behaves like any other member and posts an ordinary note to see how the guestbook handles it. The note joins the wall exactly as written: plain text, nothing unusual. This is the feature working as intended.
-
Escalate to a script
The field renders HTML, so Bob feeds it a script instead of a sentence. His probe is an image tag with a source that cannot load and an onerror handler that reads the viewer's session cookie. The image fails, the handler runs, and the browser pops up whatever the note tells it to. Here it prints the session, proof that a note can run code with the viewer's login attached.
-
It runs with the session
There it is: the note ran on its own and popped the viewer's live session cookie, with no link clicked and no file opened. A harmless print today; a silent beacon to Bob's server tomorrow. Bob has proven the wall runs whatever a note contains. Now he builds the real weapon.
-
Plant the fake login
Bob swaps the probe for a note that rewrites the page into a Session expired sign-in box, with a handler that beacons whatever is typed to his own server. To every member who loads the wall it looks like the forum simply asked them to sign in again. The credentials go straight to Bob.
-
The trap is armed
The fake login is now the newest note on the wall, and it looks just like the forum asking members to sign in again. Its handler beacons whatever is typed into it straight to Bob's server. He planted it once; every member who trusts it and signs in hands over their password.
-
Alice opens the wall
You own the guestbook's render code. This morning you catch up on the community, the same as any other admin starting their day: open your browser and go to the wall. You did not click a link or run anything. You are just loading a page you manage.
-
Hit on load
The page had barely finished loading and a popup is already printing your session cookie. You typed nothing and clicked nothing. One of Bob's notes ran the instant the wall rendered, inside your admin session. On his build the same handler would beacon that session straight to him.
-
And a fake login below it
Scroll the wall and there is a second planted note: a Session expired sign-in box that looks like the forum's own. Any member who trusts it and signs in hands their password to the attacker. These are stored notes, run by every viewer's browser. As the engineer who owns this page, you know the fix is in the render code.
Security Framework Coverage
OWASP Top 10
- A05:2025 Injection
- A03:2021 Injection
CWE
- CWE-79 Improper Neutralization of Input During Web Page Generation
CIS Controls
- CIS 16 Application Software Security
NIST CSF
- PR.AT-02 Individuals in specialized roles are provided with awareness and training so that they possess the knowledge and skills to perform relevant tasks with cybersecurity risks in mind
- PR.PS Platform Security