Cross-Site Scripting (XSS) vulnerabilities are divided into three types:
- Reflected: when payload is injected from user-provided payloads, e.g. user clicks on malicious link
- Stored: when payload is stored on server-side (e.g. database) and is injected in the page content for all users
- DOM: payload is stored in client browser
DOM-based XSS works similar to reflected one - attacker manipulates client's browser environment (Document Object Model) and places payload into page content. The main difference is, that since payload is stored in browser environment, it may be not sent on server side. This way all protection mechanisms related to traffic analysis will fail.
The payload may be stored in any DOM-tree element that is used in page content. For example, if we have following URL
https://vulnbank.com/vulnbank/online/history.php?test=somedata#hashpart, these DOM-elements may be used for attack:
document.URL: whole document URL string, e.g.
document.documentURI: same as
document.referrer: previously visited page, e.g.
location.hash: part of URL behind
location.href: same as
location.pathname: URL path, e.g.
location.search: query string from URL like
window.name: name of the window, e.g. if it is opened with
In our case, vulnerable application has the following code
$("#history-searchinfo").html( "Searching... " + decodeURIComponent(location.hash.substr(1)) );
The request to
https://vulnbank.com/vulnbank/online/history.php#<script>alert(1)</script> will inject
<script>alert(1)</script> part to content of the page and it will be executed in the browser.
However, if we will look into the traffic, request to the server is sent without
Protection against this kind of attacks should be performed on client side. The easiest way is to use DOMPurify library that suppose to sanitize payload before injecting it into the page.
It requires following steps:
DOMPurify.sanitizefunction before injecting it into the page
var clean = DOMPurify.sanitize(dirty);