Authorization disclaimer. This walkthrough is limited to the intentionally vulnerable PortSwigger Web Security Academy lab linked below. Test only systems you own or are explicitly authorized to assess.

Overview

This lab isolates Unencoded stored comment HTML. POST /post/comment persists the comment value and later inserts it into the post page as raw HTML. No output encoding prevents the stored characters from becoming a script element for every subsequent viewer.

Lab objective/setup

Open the Academy exercise at the reserved lab URL and work only in that disposable environment. The required proof is specific: Reloading the post executes the stored alert and shows the solved state.

What I observed

POST /post/comment persists the comment value and later inserts it into the post page as raw HTML. No output encoding prevents the stored characters from becoming a script element for every subsequent viewer.

Academy objective identifies unencoded stored XSS in blog comments and requires an alert for every viewer.
Academy objective identifies unencoded stored XSS in blog comments and requires an alert for every viewer.
Comment form receives the encoded script payload as inert text in the Comment field before storage.
Comment form receives the encoded script payload as inert text in the Comment field before storage.

Why it is vulnerable

The application applies protection for the wrong parser or omits a control at the decisive boundary. In this case, POST /post/comment persists the comment value and later inserts it into the post page as raw HTML. Browser parsing therefore preserves the attacker-controlled behavior instead of reducing the value to inert text or a safe URL.

Walkthrough in the authorized PortSwigger lab

First reproduce the recorded page or request and inspect the exact reflected, stored, or DOM-generated context described above. Then supply the following lab payload as data, not as active markup in this article:

1
`<script>alert(1)</script>`

No output encoding prevents the stored characters from becoming a script element for every subsequent viewer.

Browser alert dialog showing `1` when the post is revisited and the stored comment is parsed as a script element.
Browser alert dialog showing `1` when the post is revisited and the stored comment is parsed as a script element.
Solved Academy banner confirms the stored payload executed in the comment HTML context.
Solved Academy banner confirms the stored payload executed in the comment HTML context.

Verification

Reloading the post executes the stored alert and shows the solved state. This distinguishes execution from a payload merely appearing in source or being stored without reaching the required event or sink.

Impact and defensive lesson

In a real application, equivalent XSS could act with the victim’s same-origin privileges, expose readable data, or perform authenticated actions. Fix the data flow at its context: use text-only DOM APIs, JavaScript- or attribute-aware encoding, strict URL-scheme allowlists, and maintained sanitization where HTML is unavoidable. HttpOnly cookies and a restrictive CSP reduce consequences but do not repair the injection flaw.

References