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 context | Example location | Required defense |
|---|---|---|
| HTML body | Text between tags | HTML entity encoding |
| HTML attribute | Value of a quoted attribute | Attribute encoding and always quoting values |
| JavaScript string | Value inside an inline script | JavaScript string escaping or avoiding inline data |
| URL | Link target or query parameter | URL encoding and an allowlist of schemes |
| CSS | Inline style value | Strict validation, avoid user data in styles |
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.
| Type | Where the payload lives | Typical delivery | Reach |
|---|---|---|---|
| Reflected XSS | In the request, echoed by the server | Crafted link sent to the victim | One victim per click |
| Stored XSS | In the database, served to every viewer | Comment, profile field, ticket | Every user who views the content |
| DOM-based XSS | Processed entirely by client-side script | URL fragment or message read by JavaScript | Often invisible to server logs |
| Blind XSS | Stored and rendered in an internal tool | Support form viewed by staff | Privileged back-office users |
| Mutation XSS | Payload altered by browser parsing | Bypasses naive sanitizers | Depends on sanitizer and browser |
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 compared with related web attacks
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.
| Attack | What is abused | Primary defense |
|---|---|---|
| XSS | Browser trust in the site’s content | Contextual output encoding and CSP |
| CSRF | Browser sending cookies automatically | Anti-CSRF tokens and SameSite cookies |
| SQL injection | Query built from untrusted input | Parameterized queries |
| HTML injection | Markup inserted without script | The same encoding as for XSS |
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.
| Layer | Measure | What it achieves |
|---|---|---|
| Templating | Frameworks that auto-escape by default | Removes most server-side XSS |
| Safe DOM APIs | Use textContent instead of innerHTML | Prevents DOM-based injection |
| Sanitization | Vetted HTML sanitizer for rich text | Allows formatting without script |
| Content Security Policy | Nonce-based or hash-based script-src | Blocks injected inline script |
| Cookie flags | HttpOnly, Secure, SameSite | Keeps session cookies out of script reach |
| Trusted Types | Browser enforcement on dangerous sinks | Forces review of every sink assignment |
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.