Stored XSS — javascript: URI in Website Field¶
| Field | Value |
|---|---|
| Platform | PortSwigger Web Security Academy |
| Vulnerability | Stored Cross-Site Scripting (XSS) — javascript: URI |
| Difficulty | Apprentice |
| Injection Point | Website field in comment section → populates href attribute |
| Goal | Store a javascript: URI that executes when the author link is clicked |
Phase 1 — Identifying the Sink¶
Navigating to a blog post and opening the comment section:
Leaving a comment with a normal URL in the website field and inspecting the resulting HTML:
<p>
<img src="/resources/images/avatarDefault.svg" class="avatar">
<a id="author" href="www.teto.com">Teto</a> | 11 May 2026
</p>
The website field populates the href attribute of the author link directly — no scheme validation, no sanitization. This is the same sink as the DOM XSS jQuery href lab, but here the payload is stored in the database rather than read from the URL.
Phase 2 — Exploitation¶
Substituting the URL with a javascript: URI:
Website: javascript:alert('teto')
The resulting stored HTML:
<p>
<img src="/resources/images/avatarDefault.svg" class="avatar">
<a id="author" href="javascript:alert('teto')">Teto</a> | 11 May 2026
</p>
Clicking the author name triggered the alert. Lab solved.
Conclusion¶
- The website field in the comment form populated the
hrefattribute of the author link without URL scheme validation. - Submitting
javascript:alert('teto')as the website value stored thejavascript:URI in the database. - The stored payload is now served to every visitor who views the post — clicking the author name executes the JavaScript in any visitor's browser.
Key Concepts¶
Website/URL fields that populate href attributes are a classic stored XSS sink — applications frequently forget to validate that user-supplied URLs are actually HTTP/HTTPS URLs, allowing javascript: URIs to be stored and served. The href attribute is a URL field, not a string field — its value is interpreted by the browser as a navigation target, which includes JavaScript execution via the javascript: scheme.
Stored vs. reflected — same sink, different persistence:
| Reflected (jQuery href lab) | Stored (this lab) | |
|---|---|---|
| Payload lives in | URL parameter | Database |
| Executes for | Only victims sent the crafted URL | Every visitor who clicks the link |
| Requires attacker action per victim | Yes | No — fires automatically |
The fix is a strict URL scheme allowlist — validating that the href value starts with https:// or http:// before storing or rendering it blocks javascript: URIs entirely. Accepting only http and https schemes is the correct server-side control.
Stored XSS in comment sections is high impact — every subsequent visitor is affected, including administrators who moderate comments. An admin clicking the author link would execute the JavaScript in their browser with their elevated session.