September 3, 2026
Understanding DOM and API-Driven Stored XSS in OWASP Juice Shop: A Beginner-Friendly Lab…
Cross-site scripting (XSS) is a web vulnerability that occurs when an application treats untrusted input as executable browser content. In…
By Rbithin
8 min read
Cross-site scripting (XSS) is a web vulnerability that occurs when an application treats untrusted input as executable browser content. In this authorized OWASP Juice Shop lab, I explored two XSS scenarios: a client-side DOM XSS flow and an API-driven stored XSS flow.
The objective was not only to demonstrate the vulnerability, but also to understand the attacker's path, assess the potential business impact, and identify practical defensive controls. All testing was performed locally against an intentionally vulnerable training application, using accounts and systems under my control.
By the end of this walkthrough, beginners should understand the difference between reflected/DOM behavior and stored XSS, why session tokens are sensitive, and how secure output handling reduces XSS risk.
Before we begin the lab, let's define the key terms used in this walkthrough.
XSS: A vulnerability where attacker-controlled input is interpreted as active content by another user's browser.
DOM XSS: XSS caused by unsafe client-side JavaScript handling data from sources such as a URL fragment or query parameter.
Stored XSS: Malicious content is saved by the application, often in a database, and later delivered to other users.
Session token: A value that tells an application that a browser is authenticated; if mishandled, it can enable account-session abuse.
API: A backend interface used by a frontend application to retrieve or submit data.
Burp Suite Repeater: A testing tool feature used in authorized environments to resend and modify HTTP requests.
Learning objectives
- Explain how untrusted input can reach a dangerous browser sink.
- Recognize the difference between DOM XSS and stored XSS.
- Use Burp Suite to inspect HTTP requests in an authorized lab.
- Explain why browser-held authentication tokens can increase session-theft risk.
- Recommend practical prevention, detection, and response controls.
Scenario 1: Investigating DOM XSS in a Local Lab
In this scenario, I examined a Juice Shop page that processes user-controlled data from the URL. The key security question was whether the application safely handled that value before inserting it into the page's Document Object Model (DOM).
A vulnerable DOM flow can occur when data from a browser-controlled source reaches a dangerous JavaScript or HTML sink without appropriate validation, encoding, or sanitization. In a real application, this could expose users to unauthorized actions or session compromise.
To observe the result of the controlled lab demonstration, I started a local listener on my own machine in. This listener was used only inside the isolated training environment to confirm that the proof-of-concept request reached the expected destination.
Step 1
nc -lvnp 9999nc -lvnp 9999This starts Netcat in listening mode on local port 9999. The flags mean: -l listen, -v verbose output, -n skip DNS resolution, and -p 9999 select the local port.
I then used the Juice Shop challenge's authorized proof-of-concept input to demonstrate that untrusted URL-controlled data could be interpreted by the client-side application. The outcome confirmed the vulnerability in the local lab and illustrated why sensitive tokens should not be easily accessible to injected scripts.
Step 2
I opened the controlled proof-of-concept link in the local Juice Shop environment to simulate a common social-engineering entry point: a user following a malicious link. The test demonstrated how a client-side injection flaw can turn a normal browser action into a security event when unsafe input reaches executable page content.
In this controlled scenario, the proof of concept demonstrates the potential for session hijacking. If an attacker obtains a valid browser-held session token, they may be able to impersonate an authenticated user without knowing that user's password.
In this OWASP Juice Shop challenge, the Track Result page processes user-controlled URL input unsafely. The issue occurs when that input reaches a browser-rendering context without appropriate encoding or sanitization.
Controlled proof of concept: A sanitized lab-only input was used to show how URL-controlled data could be interpreted by the vulnerable client-side page. The full payload is intentionally omitted to prevent misuse.
The test input was supplied through a URL parameter. The vulnerable page processed it as active content, causing the browser to issue a request to my local listener. This demonstrates why applications must safely encode untrusted input before rendering it.
This result shows that unsafe client-side handling of URL input can expose browser-held session data; the broader security consequences are discussed in the Security impact section below.
Step 3
In the lab, I verified the security impact by showing that a captured session token could be reused to represent an authenticated browser session. This demonstrates why applications should protect tokens from client-side script access wherever possible and should use session controls that reduce replay risk.
Step 4 (optional)
In this optional OWASP Juice Shop bonus challenge, I used the lab-provided proof of concept to trigger an audio effect. This shows that XSS can alter page behavior, even when the payload is not designed to access sensitive data.
Scenario 2: API-Driven Stored XSS with Burp Suite
The second scenario examined whether a backend API accepted attacker-controlled product data and whether that data was later rendered unsafely in the web interface. This is important because API endpoints are part of the application's attack surface, even when the visible frontend does not provide an obvious input field.
I used Burp Suite in the authorized local lab to inspect normal authenticated traffic, identify the relevant product-related API request, and test whether submitted data was safely handled.
Step 1
Launch Burp Suite in Kali Linux to inspect HTTP traffic from the authorized local OWASP Juice Shop environment.
Generate normal application traffic: Log in to the local Juice Shop account through Burp Suite's browser and visit relevant pages so that legitimate API requests appear in HTTP history.
Step 2
Identify the API request: Review requests related to user identity, products, or product creation/update functionality.
In Proxy → HTTP history, I reviewed requests to endpoints such as /rest/user/whoami and /api/Products to identify authenticated product-related API traffic.
Step 3
Send a request to Repeater: Use Repeater to inspect the request structure and test controlled input handling without repeatedly interacting with the browser interface.
Step 4
Validate server response: Confirm whether the API accepts the test data and observe the HTTP status code, response body, and stored application behavior.
Here we set the target such as the products page where an authorization token is necessary to make requests and add payload.
Then click send upon which if successful we get a 201 code with status "success".
If the backend API accepts this request without sanitizing or stripping out HTML tags, it will save this exact malicious string directly into the database with the product details.
The actual attack doesn't happen when you send the data. It happens later when a user views the website.
When a user browses the catalog, the frontend retrieves product data from the backend. If the product description contains untrusted HTML and the application renders it without safe output encoding or sanitization, the browser may interpret it as active content.
The browser reads the product description, sees the
If an application stores untrusted HTML and later inserts it into the page without safe output encoding or sanitization, browser behavior may allow active content to execute. The exact outcome depends on the injected markup, browser protections, application code, and defenses such as Content Security Policy.
Step 5
Validate rendering behavior: View the affected product through the normal application interface to determine whether stored content is rendered as text or interpreted as active browser content.
While here simple alert() box is used to prove a vulnerability exists, an attacker in the real world would replace alert('xss') with malicious code designed to exploit anybody who queries the database for products.
This payload is permanently stored in the database, every single user who visits that specific product page will automatically execute the script in their own browser. This could be used to:
- Steal session tokens or authentication keys out of the victim's browser memory.
- Force the victim's browser to perform unauthorized actions like modifying account details or making purchases without their knowledge.
- Deface the website layout dynamically for anyone viewing that item catalog.
If a privileged user views an infected product page, stored XSS may allow actions to be performed within that user's browser session. Depending on the application's authorization controls and available functions, this could lead to unauthorized account changes, exposure of sensitive on-page data, or privilege escalation.
Security impact
Stored XSS is particularly serious because the malicious content can affect every user who views the infected record. If an administrator, support employee, or other privileged user visits the affected page, the vulnerability may enable actions under that user's browser session.
In a real organization, the impact could include unauthorized changes to account settings, fraudulent actions performed through the victim's active session, exposure of sensitive on-page data, or disruption of the application's user interface. The actual risk depends on the application's session design, privilege model, and browser-side protections.
How to prevent XSS
XSS prevention requires layered controls rather than a single filter. The most important principle is to treat all user-controlled data as untrusted, including data submitted through APIs, URL parameters, forms, headers, and database records.
Use context-aware output encoding: Encode data correctly for HTML, attributes, URLs, JavaScript, and CSS contexts.
Avoid unsafe DOM APIs: Minimize use of sinks such as
innerHTML; prefer safer APIs such astextContentwhen rendering plain text.
Sanitize necessary HTML: If rich text is genuinely required, sanitize it with a maintained, allowlist-based HTML sanitizer.
Validate data server-side: Validate expected formats and lengths on the server; client-side validation alone is not a security boundary.
Apply Content Security Policy: Use a restrictive CSP to reduce the impact of injected scripts.
Protect session tokens: Prefer
HttpOnly,Secure, and appropriately configuredSameSitecookies where suitable; avoid exposing long-lived tokens to JavaScript unnecessarily.
Use authorization controls: Ensure low-privileged accounts cannot call sensitive API functions simply by changing a request.
Log and monitor anomalies: Alert on unusual API writes, suspicious HTML-like input, repeated rejected requests, and unexpected account actions.
This exercise strengthened my understanding of web-application attack surfaces beyond visible input forms. I practiced analyzing client-side behavior, inspecting API traffic with Burp Suite, validating controlled security impact in OWASP Juice Shop, and translating technical findings into defensive recommendations.
The main lesson was that web security must be addressed across the full data lifecycle: input enters through the browser or API, is processed by the backend, stored in databases, returned to the client, and finally rendered in a browser context. Any unsafe transition can create a vulnerability.