What Is XSS? Cross-Site Scripting Explained
Cross-site scripting (XSS) injects malicious scripts into pages other users view. How stored, reflected, and DOM-based XSS work, and how to prevent them.
Cross-site scripting (XSS) is a vulnerability that lets an attacker inject malicious JavaScript into a page viewed by other users, so the script runs in their browser with their session and permissions. Because the browser can’t tell attacker-supplied script from the site’s own code, it executes with full access to cookies, local storage, and anything else the page can touch — including, in the worst case, session tokens that let the attacker impersonate the victim entirely.
The three flavors
XSS is usually grouped by where the malicious payload lives and how it gets to the victim’s browser:
- Stored XSS. The payload is saved server-side — a comment field, a user bio, a forum post — and then served to every visitor who views that page. It’s the most dangerous variant because it requires no interaction from any individual victim beyond simply visiting a page that already exists.
- Reflected XSS. The payload lives in the request itself, typically a URL query parameter, and gets echoed back unescaped in the response. Exploiting it requires tricking a specific victim into clicking a crafted link, since the malicious part of the URL has to actually be sent.
- DOM-based XSS. The vulnerability lives entirely in client-side JavaScript that writes untrusted data into the page — for example, reading
location.hashand inserting it intoinnerHTML— without any server involvement at all. The payload never even has to touch the server to do damage.
What an attacker can actually do
Once a script runs in a victim’s authenticated browser session, the attacker effectively borrows that session. Common outcomes include stealing session cookies or tokens to hijack the account, silently making authenticated requests on the victim’s behalf (which can bypass some CSRF defenses since the script is running as the legitimate user), redirecting the page to a phishing site, defacing content, or logging keystrokes. If a session is represented as a JWT stored somewhere JavaScript can read, XSS is exactly the kind of vulnerability that turns “readable by the client” into “readable by an attacker.”
Preventing XSS
Defense against XSS is layered, because no single control catches every case:
- Escape output by context. Text inserted into HTML, an HTML attribute, a
<script>block, or a URL each need different escaping rules — the same string that’s safe in one context can be dangerous in another. - Avoid raw HTML injection with untrusted input. Prefer
textContentoverinnerHTML, and treatdangerouslySetInnerHTML-style escape hatches in frameworks as a deliberate, reviewed exception rather than a default. Most modern frameworks (React, Vue, Svelte) auto-escape interpolated values by default, which closes off a large share of accidental XSS. - Set a Content-Security-Policy. A well-configured CSP can block inline scripts and restrict which origins scripts may load from, so even a successful injection often has nowhere to execute from.
- Mark cookies HttpOnly and Secure. An
HttpOnlycookie can’t be read by JavaScript at all, so even a successful XSS payload can’t exfiltrate the session cookie directly — this doesn’t stop the attack, but it removes one of its most damaging outcomes. - Sanitize rich text with an allowlist. If users are allowed to submit formatted content (a comment editor, a markdown preview), run it through a sanitization library that allows specific known-safe tags and attributes rather than trying to block a list of known-bad ones.
Where it sits relative to other browser security controls
XSS exploits a trust boundary that other browser mechanisms also guard, which is why it’s worth understanding alongside them rather than in isolation. The same-origin policy and CORS govern which origins can read a page’s data at all — XSS sidesteps that by running as the legitimate origin in the first place, since the injected script executes with the victim’s own permissions rather than trying to reach across origins. A WAF can catch some obviously malicious payloads in transit as a defense-in-depth layer, but it’s not a substitute for escaping and sanitizing output correctly at the application layer, since a sufficiently obfuscated payload can slip past pattern-based filtering.
The takeaway
XSS lets an attacker’s script run as if it were the site’s own code, in the victim’s authenticated browser — the danger comes from where the payload lives (stored, reflected, or DOM-based), but the fix is the same across all three: escape output by context, avoid raw HTML injection with untrusted input, and layer on a Content-Security-Policy and HttpOnly cookies so a successful injection has less to work with even when one slips through.
Tagged
Keep reading
Chisato · · 4 min read What Is a Content Security Policy (CSP)?
A Content Security Policy is an HTTP header that restricts what scripts and resources a page can load, blocking most XSS attacks by default.
Takina · · 4 min read What Is CORS? Cross-Origin Requests, Explained
CORS lets a server opt in to cross-origin browser requests, relaxing the same-origin policy in a controlled way. Why it exists and how to fix CORS errors.
Takina · · 4 min read TypeScript Abstract Classes, Explained
Abstract classes in TypeScript define shared implementation plus methods subclasses must fill in. How they differ from interfaces and when to reach for them.