loader
high risk OSI Layer 7 / Browser current

Cross-Site Scripting

Attacker-controlled script executes in another user's browser under the origin of the vulnerable site, inheriting that user's session and permissions.

Defender-oriented briefing. This page describes behavior, observable signals, prevention and response. Operational attack instructions are intentionally excluded.

How it works

Output is placed into HTML, attribute, JavaScript, URL or DOM contexts without the encoding appropriate to that context, so data written by one user is parsed as code in another user's browser.

Why the browser cannot help you

Cross-site scripting is an output problem misfiled as an input problem. The browser receives a document and parses it. It has no way to know that one span of characters came from your template and another came from a comment field — both arrived in the same response, under the same origin, with the same authority.

That authority is the whole impact. Script running in your origin can read the DOM, issue requests carrying the user's cookies, modify what the page displays, and capture what the user types. It is not "a popup"; it is the attacker acting as the user, inside the user's session, with the application unable to distinguish those actions from legitimate ones.

Filtering input is the intuitive response and the wrong layer. The same stored value may be rendered into HTML on one page, a JavaScript string on another, and a URL attribute on a third — each requiring different encoding. A filter at the input boundary cannot know which, and stripping tags on the way in corrupts legitimate data while still missing contexts.

The three shapes

Reflected XSS returns attacker input in the immediate response — a search term echoed onto the results page. It requires the victim to follow a crafted link, which makes it a phishing-delivered attack.

Stored XSS persists the payload and serves it to everyone who views the affected record. It requires no link and no interaction beyond visiting a normal page, which makes it substantially more severe. A stored payload in a widely viewed field is close to worst case, and profile and comment fields are the usual homes.

DOM-based XSS never involves the server in the injection. Client-side JavaScript reads something attacker-influenced — a URL fragment, a query parameter, a message from another window — and writes it into the DOM in a way that executes. Server-side encoding is irrelevant, and server logs may show nothing unusual at all, which is why this variant survives review so often.

Context is the whole problem

Correct encoding depends entirely on where the value lands, and the rules genuinely differ.

  • HTML body — encode the characters that begin a tag or entity.
  • Attribute values — encode quotes, and always quote attributes; an unquoted attribute can be escaped with a space alone.
  • Inside a script block — HTML encoding does not apply and does not help. Serialise the value as JSON and let the parser handle it, or avoid embedding server data in script entirely.
  • URL parameters — percent-encode, and validate the scheme separately; a value beginning with a script scheme executes when placed in an href regardless of encoding.
  • CSS contexts — a separate set of rules again, and a good reason not to build stylesheets from user data.

Rich text, where it gets hard

Some applications must accept HTML from users — comment formatting, article bodies, embedded media. This is the genuinely difficult case, because escaping everything defeats the feature.

The only workable approach is to parse the input into a document, walk it, and rebuild it from an allow-list of permitted elements and attributes. Anything not explicitly allowed is discarded. Regular-expression sanitisers fail here reliably; HTML is not a regular language and the parsing differences between a sanitiser and a browser are exactly where bypasses live.

Use a maintained library and keep it updated. Sanitiser bypasses are found regularly, and a sanitiser that has not been updated in years is a sanitiser with published bypasses.

Defence in depth that actually helps

A strict Content Security Policy is the most valuable secondary control, because it changes the outcome of a bug you have not found yet. A policy that disallows inline script and restricts sources means an injected tag frequently does nothing at all.

It has to be strict to be worth deploying. A policy permitting unsafe-inline provides essentially no protection against the most common payloads, and policies drift toward that value under delivery pressure. Nonce or hash-based policies are more work and are the ones that hold.

HttpOnly on session cookies removes the simplest objective — script cannot read the cookie. It does not stop the attacker acting through the user's browser, so it reduces impact rather than removing it.

Collect CSP violation reports. They surface both real injection attempts and the places your own code violates the policy, which is usually where the next bug will be.

Also known as

XSS stored XSS reflected XSS DOM XSS

What defenders may observe

  • Unexpected script execution or DOM mutation
  • User-generated content triggers browser security reports
  • Session or action abuse follows a viewed page rather than a submitted form
  • Content Security Policy violation reports cluster on one route

Prevention and mitigation

  • Encode output for the specific context it lands in, not generically
  • Use templating that escapes by default and treat raw-output helpers as review-worthy
  • Sanitise rich text with a well-maintained allow-list library, never a regular expression
  • Prefer textContent over innerHTML when building DOM in JavaScript
  • Deploy a strict Content Security Policy as defence in depth
  • Set HttpOnly on session cookies so script cannot read them

Initial response priorities

  • Contain the vulnerable route or feature.
  • Preserve requests, traces and affected records.
  • Fix the root control failure, test adjacent paths and rotate exposed secrets.

Use your approved incident-response plan and adapt decisions to your environment. This is educational guidance, not a substitute for live incident leadership.

Authoritative references