The three types of cross-site scripting, how they differ, and why DOM XSS is the trickiest to find.
| Stored | Reflected | DOM-based | |
|---|---|---|---|
| Payload stored? | Yes — in the database | No — in the URL/request | No — in the DOM |
| Server involved? | Server stores and serves it | Server reflects it back | Server never sees it |
| Victim interaction | Just visits the page | Clicks a crafted link | Clicks a crafted link |
| Persistence | Hits every visitor | One-time per click | One-time per click |
| Typical location | Comments, profiles, messages | Search results, error pages | Client-side JS reading URL fragments |
| Detection | Easy — in stored HTML | Medium — parameter fuzzing | Hard — requires JS analysis |
| WAF effectiveness | Can detect on input | Can detect on reflection | Often invisible to WAFs |
The payload gets saved — in a database, a comment field, a user profile. Every person who views that page gets hit. This is the most dangerous type because it doesn't require the victim to click anything special. The recent Jira Work Management stored XSS could have led to full organization takeover.
The payload is in the URL or form submission and the server echoes it back in the response without sanitizing it. The victim has to click a malicious link. Less impactful than stored because it's one victim at a time, but still earnable in bug bounty — especially if you can chain it with other vulnerabilities.
This one lives entirely in the browser. The server response is clean. The vulnerability is in client-side JavaScript that reads from an attacker-controllable source (like location.hash or document.referrer) and passes it to a dangerous sink (like innerHTML or eval()). WAFs can't see it because the payload never hits the server. You need to read the JavaScript to find it.
In bug bounty, reflected XSS is the easiest to find with automation. Stored XSS pays better. DOM XSS is where the competition is lowest because most hunters don't bother reading JavaScript.