Cross-Site Scripting: What an Attacker Actually Does With It

Cross-site scripting injects attacker-controlled script into a page that other users load. The script then runs inside that site's origin, with the same access the site's own code has — which is the part people underestimate, because "someone can run JavaScript" sounds less serious than it is.

The three types

Stored XSS is the most serious. The payload is saved on the server — in a comment, a profile field, a support ticket, a filename — and served to everyone who views that content. One injection, many victims, no interaction required beyond visiting a legitimate page. Stored XSS in an administrative interface is particularly severe, because the victim is an administrator.

Reflected XSS takes the payload from the request and includes it in the response — typically a search term or an error message echoed back. It affects only the person who made the request, so it requires delivering a crafted link, usually with the destination hidden as under obfuscated links.

DOM-based XSS never involves the server in the injection. Client-side code reads something attacker-controlled — the URL fragment, a query parameter, a message from another window — and writes it into the page in a way that executes. Server-side output encoding does nothing about it, and server logs may not even show the payload, since a fragment is never sent.

What the script can reach

This is the part worth being concrete about.

Session tokens, unless the cookie is marked HttpOnly — which is why that flag matters so much, as noted under session hijacking. With the token, the attacker becomes the user elsewhere.

Any action the user can perform. Even with HttpOnly set, the script runs in the user's session and can simply make requests as them — change the email address, add a payment method, create an administrative account. It does not need to steal the cookie when it can use it.

Everything on the page, including data rendered client-side, and anything the application fetches from its API.

Keystrokes and form input, by attaching listeners — so a login form on the affected origin can be captured directly.

The page's appearance, which allows a convincing fake login prompt on the real site at the real domain, with a valid certificate. That is the scenario where XSS becomes credential theft that no URL inspection detects.

Persistence within the session, hooking navigation so the script survives page changes, and pivoting to reachable internal services from the victim's browser.

The controls

Contextual output encoding is the primary fix. Untrusted data must be encoded for the context it lands in — HTML body, HTML attribute, JavaScript, URL and CSS each require different encoding, and applying the wrong one is as ineffective as applying none. Use the framework's contextual auto-escaping and be careful with the functions that bypass it, which exist for a reason and are the usual source of the flaw.

Content Security Policy is the strongest defence in depth available. A policy that disallows inline scripts and restricts sources means an injected script does not execute even where the injection succeeded. Nonce or hash-based policies are the workable form; a policy permitting unsafe inline defeats the purpose. It takes real effort to deploy on an existing application and it is the control that turns an exploitable flaw into a blocked one.

HttpOnly, Secure and SameSite on session cookies limit what a successful injection achieves.

Avoid dangerous sinks in client-side code — the functions that parse strings into markup or execute them — and use the safe alternatives that set text rather than HTML. Trusted Types enforce this where supported.

Input validation is worth having and is not the fix, because the same input may be safe in one context and dangerous in another. Encode on output, validate on input, and do not substitute one for the other.

A WAF blocks common payloads and is bypassed by encoding variation, so it buys time — the same position as with every injection family.

Testing and detection

Automated scanners find reflected and many stored cases reliably. DOM-based XSS is harder and frequently needs manual review of client-side code, tracing untrusted sources to dangerous sinks — which is why the manual work described under intercepting proxies still matters.

In production, CSP violation reporting is the useful signal: a report indicates either a policy that needs adjusting or an injection attempt, and both are worth knowing about.

Exam angles

Distinguish stored (persisted server-side, affects all viewers), reflected (echoed from the request, requires a crafted link) and DOM-based (client-side code writes untrusted data into the page, server never sees it). Know that injected script runs in the site's origin with the user's privileges, so it can act as the user even when the cookie is HttpOnly. Name contextual output encoding as the primary fix and Content Security Policy as the strongest mitigation.

Comments