Cross-Site Scripting
Definition
Cross-site scripting, abbreviated XSS to avoid a collision with CSS, comes in three shapes. Stored XSS means the hostile text is saved somewhere and served to later visitors. Reflected XSS means it arrives in a URL — usually a query string — and is echoed straight back into the page. DOM-based XSS never involves the server at all: the page reads something from location.hash or location.search and writes it into the document with innerHTML or document.write. All three end the same way, with attacker-chosen JavaScript running under your origin, which under the same-origin policy is trusted to read the page, its storage and its cookies. The root cause is always the same: data was placed into HTML without being escaped first.
Why It Matters
Most people publishing a generated page assume they have no XSS surface because there is no server and no database. DOM-based XSS does not need either. A single-file page that reads a name from the query string and drops it into innerHTML to say hello is vulnerable the moment someone crafts a link — and that link looks like your address, arrives from you, and is trusted accordingly. What runs next can rewrite the page, harvest whatever is typed into it, or read every key in localStorage. The fix is usually a one-line change from innerHTML to textContent, which is a smaller job than the risk suggests.
How It Works
The browser makes no distinction between markup the author wrote and markup assembled at runtime; if a string containing a script tag or an onerror attribute reaches the HTML parser, it runs. Safe handling means escaping the five HTML-significant characters, or better, avoiding HTML assembly entirely: set textContent, use createElement and setAttribute, and keep URLs out of href values unless the scheme is checked. Never pass untrusted text to eval, to setTimeout as a string, or to a framework's raw-HTML escape hatch. A Content Security Policy without unsafe-inline is the backstop, blocking injected inline scripts even when escaping was missed, and the sandbox attribute on an iframe limits what embedded content can do. None of these replace escaping; they reduce what a miss costs.
Real-World Example
A marketer publishes a personalised proposal page at nova-offer.99helpers.site that reads a client name from the query string and writes it into the heading with innerHTML. A link with markup instead of a name in that parameter turns the page into whatever the sender wants, at an address the recipient has every reason to trust. Switching the assignment to textContent, and adding a policy of script-src 'self' 'unsafe-inline', removes the injection while leaving the personalisation working.
Common Mistakes
- ✕Assuming a static page cannot have XSS — DOM-based injection lives entirely in the browser and needs no server
- ✕Escaping on the way in but not on the way out, so text stored safely is still written raw into the page later
- ✕Using innerHTML for plain text because it was quicker to type than textContent, which is the single most common source of the flaw
- ✕Treating a Content Security Policy as a substitute for escaping rather than a second layer behind it
Related Terms
Content Security Policy
A response header that tells the browser which sources a page is allowed to load scripts, styles, images and frames from. Anything outside the policy is blocked before it runs.
Same-Origin Policy
The browser rule that keeps code loaded from one origin from reading data belonging to another. It is the foundation the rest of web security is built on.
Inline JavaScript
JavaScript written inside the HTML document — in a script element with no src attribute, or in an attribute such as onclick — rather than loaded from a .js file. It keeps a generated page to one file.
Query String
A query string is the part of a URL after the question mark, carrying key-value pairs such as src=newsletter and page=2. It passes small pieces of data along with the request.
Static Site
A static site is a website made of finished files — HTML, CSS, JavaScript, images — that are sent to the browser exactly as they are stored. Nothing is assembled on the server when a visitor arrives.
Put a file online in seconds
Drop in a document, an image, a page or a whole static website and share the link — free, with no build step and no server to set up.
Host a file free →