DOM-Based XSS — jQuery href Sink via location.search¶
| Field | Value |
|---|---|
| Platform | PortSwigger Web Security Academy |
| Vulnerability | DOM-Based Cross-Site Scripting (XSS) |
| Difficulty | Apprentice |
| Source | location.search (returnPath parameter) |
| Sink | jQuery .attr("href", ...) |
| Goal | Inject a javascript: URI into the Back link's href attribute |
Key Concepts¶
location.search — the query string portion of the current URL, starting with ?. In /feedback?returnPath=/home, location.search returns ?returnPath=/home.
href — the HTML attribute that defines where a link points. When a user clicks an anchor tag, the browser navigates to the href value — or if the value is a javascript: URI, it executes that JavaScript instead of navigating.
document.cookie — a JavaScript property that returns all cookies associated with the current page as a string.
Phase 1 — Reconnaissance¶
The feedback page URL contains a returnPath parameter that controls where the Back link points:
/feedback?returnPath=/
Inspecting the page source in DevTools reveals the vulnerable JavaScript:
$(function() {
$('#backLink').attr("href",
(new URLSearchParams(window.location.search)).get('returnPath')
);
});
This jQuery code reads the returnPath value from the URL query string and assigns it directly to the href attribute of the Back link — no sanitization. Whatever we put in returnPath becomes the href.
Inspecting the Back link element confirms it:
<a id="backLink" href="/">Back</a>
Phase 2 — Exploitation¶
Injecting a javascript: URI into returnPath:
/feedback?returnPath=javascript:alert(document.cookie)
The DOM now looks like:
<a id="backLink" href="javascript:alert(document.cookie)">Back</a>
Clicking the Back link executes alert(document.cookie) — lab solved.
Why javascript: URIs Work in href¶
HTML allows href to contain a javascript: URI — when clicked, instead of navigating to a URL, the browser evaluates the JavaScript expression after the colon. This is a legitimate feature that has existed since early browsers, but it becomes a vulnerability when attacker-controlled data flows into an href attribute without validation.
<a href="javascript:alert(1)">Click me</a> ← executes alert(1) on click
<a href="/safe/path">Click me</a> ← navigates to /safe/path
The key difference from the innerHTML lab is the sink. Here the sink is jQuery's .attr("href", ...) — not innerHTML. <script> tags and onerror payloads are irrelevant in this context. The only attack surface is what goes into the href attribute, and javascript: is the correct vector for that sink.
How the Full Attack Chain Works¶
1. Attacker crafts URL:
/feedback?returnPath=javascript:alert(document.cookie)
2. Victim visits the page
3. jQuery reads location.search:
new URLSearchParams(window.location.search).get('returnPath')
→ returns: "javascript:alert(document.cookie)"
4. jQuery sets the href:
$('#backLink').attr("href", "javascript:alert(document.cookie)");
→ DOM is now: <a href="javascript:alert(document.cookie)">Back</a>
5. Victim clicks "Back"
6. Browser executes: alert(document.cookie)
Conclusion¶
- The
returnPathURL parameter was read by jQuery viaURLSearchParams(window.location.search)and assigned directly to the Back link'shrefattribute with no sanitization. - Injecting
javascript:alert(document.cookie)as thereturnPathvalue set thehrefto ajavascript:URI. - Clicking the Back link executed the JavaScript in the browser context.
Key Concepts¶
The sink determines the payload format — this is the most important concept across all XSS labs:
| Sink | Payload type | Example |
|---|---|---|
document.write() |
Break attribute + script | "><script>alert(1)</script> |
innerHTML |
Event handler | <img src=0 onerror=alert(1)> |
href attribute |
javascript: URI |
javascript:alert(1) |
Always identify the sink before crafting the payload. Using an onerror payload against an href sink accomplishes nothing — the tag is never rendered by innerHTML, it becomes literal href content.
javascript: URIs execute when the link is clicked, not on page load — this makes the attack slightly less severe than an auto-executing payload, but it is still fully exploitable via social engineering ("click the Back button to return").
jQuery .attr() with untrusted input is a DOM XSS sink — jQuery's $ selector and attribute manipulation functions do not sanitize input. They pass values to the DOM directly.
The safe fix is to validate returnPath is a relative URL before assigning it to href. A strict allowlist — for example, only paths starting with / — blocks javascript: URIs entirely. Never assign unvalidated URL parameters to href.