Safeguard
Vulnerabilities

XSS Scripting Attacks: A Refresher for Engineers

XSS scripting attacks still make the OWASP Top 10 more than two decades after they were first documented, mostly because the fix is context-dependent and easy to get almost-right.

Yukti Singhal
Head of Product
5 min read

XSS scripting attacks work by getting an attacker's JavaScript to execute in a victim's browser within the security context of a trusted site, letting that script read cookies, make authenticated requests, or modify the page as if it were legitimate application code. It's one of the oldest vulnerability classes on the books and still routinely makes the OWASP Top 10, not because the concept is hard to explain but because the fix depends entirely on where the untrusted data ends up — a sanitization approach that's correct for HTML body content is wrong for a JavaScript string, an HTML attribute, or a URL, and that context-dependence is where real-world defenses quietly fail.

What are the three main types of XSS?

Reflected XSS happens when an application takes input directly from a request — a search query, a URL parameter — and includes it in the response page without proper encoding, so the attacker crafts a link that, when clicked, executes their script in the victim's session. Stored XSS is more dangerous because the malicious payload gets saved on the server (a comment, a profile field, a support ticket) and then served to every user who views that page, turning a single successful injection into a persistent, self-propagating problem until it's found and cleaned up. DOM-based XSS is the newest of the three categories and the one most often missed by traditional scanners, because the vulnerability never touches the server at all — it happens entirely in client-side JavaScript that reads something like location.hash or document.URL and writes it into the page through an unsafe sink like innerHTML, so a scanner that only inspects server responses won't see it.

Why does sanitization keep going wrong in practice?

The most common mistake is applying one encoding scheme everywhere instead of matching the encoding to the output context. HTML entity encoding is correct for text inserted into an HTML body but does nothing to protect a value inserted into a <script> block or an inline event handler like onclick, and a value placed inside a URL needs percent-encoding, not HTML encoding, or an attacker can still break out of the intended attribute. Frameworks that auto-escape by default (React, Vue, most modern templating engines) have cut down on classic reflected and stored XSS significantly, but they create a new failure mode: developers explicitly opt out of escaping — dangerouslySetInnerHTML in React, v-html in Vue — usually to render some trusted-looking HTML from a CMS or rich-text editor, and that trusted-looking source is exactly where injected payloads tend to originate.

How is XSS actually found during testing?

Static analysis traces the path from an untrusted source to a dangerous sink through the codebase, flagging places where user-controlled data reaches something like innerHTML or a template literal without going through an established sanitizer function first. Dynamic testing takes the opposite approach: it actually submits payloads — including canary strings designed to reveal exactly where and how a value gets reflected — into every input the application exposes, then checks whether the payload executed or appeared unescaped in the response. A capable DAST tool covers this second approach at scale, running through login flows and multi-step forms rather than only the handful of endpoints a human tester would think to check first, which matters because a lot of real XSS lives in secondary features like profile bios or file upload metadata that never get manual review.

What actually stops XSS at the platform level?

Content Security Policy is the strongest layer-two defense: a well-configured CSP header can block inline script execution entirely, so even if an attacker successfully injects a <script> tag, the browser refuses to run it. HttpOnly cookies limit the blast radius of a successful XSS by preventing injected JavaScript from reading session cookies directly, which doesn't stop the injection but does stop the most common thing attackers do with it. Neither of these substitutes for fixing the underlying injection point — they're defense in depth, not a fix — and teams that rely on CSP alone while leaving the actual sanitization bug in place are one CSP misconfiguration away from the original vulnerability being fully exploitable again.

How does this fit into a broader vulnerability program?

XSS findings tend to cluster in the same handful of code paths — rich text rendering, search result pages, error messages that echo back user input — so a program that fixes one instance without checking for the pattern elsewhere in the codebase ends up playing whack-a-mole for years. Running both static and dynamic testing catches the two different flavors this bug takes (a code-level pattern versus a runtime-observable behavior), and teams building out a broader SCA and application security program should expect XSS to keep surfacing in new features unless sanitization is enforced as a shared library function rather than left to individual developers to reimplement per form field.

FAQ

Is XSS still relevant with modern frameworks that auto-escape output?

Yes, mainly through explicit escape-hatch APIs like dangerouslySetInnerHTML or v-html, and through DOM-based XSS in client-side JavaScript that frameworks don't automatically protect against.

What's the difference between reflected and stored XSS in terms of severity?

Stored XSS is generally more severe because the payload persists and executes for every visitor to the affected page, rather than requiring a victim to click a specific crafted link.

Does Content Security Policy fully prevent XSS?

No — CSP is a strong mitigation that blocks many exploitation techniques but doesn't fix the underlying injection vulnerability. A misconfigured or overly permissive CSP can still be bypassed.

Can automated scanners reliably find DOM-based XSS?

Modern DAST tools that execute JavaScript in a real browser context can find many DOM-based XSS cases, but they still often need to be paired with static analysis that traces client-side data flow explicitly.

Never miss an update

Weekly insights on software supply chain security, delivered to your inbox.