XSS

On this page 7

XSS (cross-site scripting) is an application security vulnerability in which untrusted input is delivered to a browser as executable script within the context of a trusted website. Because the injected code runs with the same origin as the legitimate page, it can read cookies that are not protected, call APIs with the victim’s session, alter the page and capture keystrokes.

In cybersecurity, XSS is classed as an injection flaw, the same family as SQL injection, and it appears in the OWASP Top 10 under the injection category. The root cause is always the same: the application mixes data and code without encoding the data for the context in which the browser will interpret it.

How cross-site scripting works

A browser builds a page from HTML, attributes, URLs, CSS and JavaScript, and each of these contexts has its own syntax. When an application inserts user-controlled text into one of them without context-appropriate output encoding, special characters such as angle brackets, quotes or backslashes can break out of the data context and start new markup or script.

Once script runs, the same-origin policy treats it as part of the vulnerable site. The attacker does not need to break any cryptography; the browser simply executes what the server or client code assembled.

Output contextExample locationRequired defense
HTML bodyText between tagsHTML entity encoding
HTML attributeValue of a quoted attributeAttribute encoding and always quoting values
JavaScript stringValue inside an inline scriptJavaScript string escaping or avoiding inline data
URLLink target or query parameterURL encoding and an allowlist of schemes
CSSInline style valueStrict validation, avoid user data in styles
Injection contexts and the encoding each requires

Types of XSS

XSS variants differ in where the payload is stored and which code inserts it into the page. The distinction matters because each type needs different detection and a different fix location.

TypeWhere the payload livesTypical deliveryReach
Reflected XSSIn the request, echoed by the serverCrafted link sent to the victimOne victim per click
Stored XSSIn the database, served to every viewerComment, profile field, ticketEvery user who views the content
DOM-based XSSProcessed entirely by client-side scriptURL fragment or message read by JavaScriptOften invisible to server logs
Blind XSSStored and rendered in an internal toolSupport form viewed by staffPrivileged back-office users
Mutation XSSPayload altered by browser parsingBypasses naive sanitizersDepends on sanitizer and browser
Main categories of cross-site scripting

Stored XSS is usually the most severe because it needs no social engineering and can spread automatically. DOM-based XSS has grown in importance with single-page applications, where dangerous sinks such as innerHTML, document.write and eval receive data from sources like location.hash.

XSS is often confused with CSRF and SQL injection. The key difference is where the malicious instruction executes: in the victim’s browser for XSS, in the database for SQL injection, and as a forged request for CSRF.

AttackWhat is abusedPrimary defense
XSSBrowser trust in the site’s contentContextual output encoding and CSP
CSRFBrowser sending cookies automaticallyAnti-CSRF tokens and SameSite cookies
SQL injectionQuery built from untrusted inputParameterized queries
HTML injectionMarkup inserted without scriptThe same encoding as for XSS
XSS and neighboring application security flaws

An important consequence is that XSS defeats CSRF protection. Script running in the site’s origin can read the anti-CSRF token from the page and submit any request the user could make, so fixing XSS is a precondition for other browser-side controls to hold.

Preventing XSS in practice

Effective prevention is layered. Encoding and safe templating remove the vulnerability, while browser-side controls limit the damage of any flaw that slips through.

LayerMeasureWhat it achieves
TemplatingFrameworks that auto-escape by defaultRemoves most server-side XSS
Safe DOM APIsUse textContent instead of innerHTMLPrevents DOM-based injection
SanitizationVetted HTML sanitizer for rich textAllows formatting without script
Content Security PolicyNonce-based or hash-based script-srcBlocks injected inline script
Cookie flagsHttpOnly, Secure, SameSiteKeeps session cookies out of script reach
Trusted TypesBrowser enforcement on dangerous sinksForces review of every sink assignment
Defense layers against cross-site scripting

A strict CSP with nonces is a strong second line, but it is not a substitute for encoding. Policies that allow unsafe-inline or broad host allowlists are frequently bypassed, so CSP should be tested with the same rigor as application code.

Common mistakes and misconceptions

  • Input filtering alone is insufficient: blocklists of words such as script miss event handlers, encodings and new browser features.
  • Encoding for the wrong context leaves holes, for example HTML encoding a value placed inside a JavaScript string.
  • Framework escape hatches such as functions that render raw HTML reintroduce XSS when fed user data.
  • HttpOnly cookies do not stop XSS; script can still act as the user without reading the cookie.
  • Internal tools are not safe by default; blind XSS targets staff dashboards with high privileges.

In plain words

Imagine a community notice board where the staff pin up whatever visitors hand them. A visitor hands in a note that looks like an official instruction, and readers follow it because it hangs on the trusted board.

XSS is the same trick on a website. The site displays something an attacker wrote, and the browser treats it as the site’s own instructions. The fix is for the site to always show visitor text as plain text, never as something the browser can run.

A real-world example

An online marketplace allows sellers to write product descriptions with basic formatting. A developer adds a feature that inserts the description into the page with innerHTML and relies on a homemade filter that removes the word script. An attacker creates a listing whose description contains an image tag with an onerror handler that sends the visitor’s session data and account details to an external server.

Within hours, buyers who view the listing have their sessions abused to change delivery addresses. The stored XSS reaches every visitor without a single phishing email. The security team removes the listing, replaces the filter with a vetted sanitizer, switches rendering to safe DOM APIs and deploys a nonce-based Content Security Policy. A follow-up penetration test confirms that the same payload is now displayed as harmless text.