GlossaryCross-Site Scripting

Cross-Site Scripting

Explore Article +

Key Takeaways

  • Cross-site scripting (XSS) lets an attacker run malicious JavaScript in another user’s browser by injecting it through an application that fails to sanitize input.
  • The three main types are stored, reflected, and DOM-based, differing in where the malicious script lives and how it reaches the victim.
  • DOM-based XSS never touches the server, which is why server-side filters and many scanners miss it.
  • A successful XSS attack can steal sessions, capture keystrokes, and take over accounts, not just deface a page.
  • Prevention combines output encoding, a strong Content Security Policy, and testing that exercises how the app handles untrusted input.

What Is Cross-Site Scripting?

Cross-site scripting is a vulnerability that lets an attacker inject malicious scripts into content that other users load in their browsers. The victim’s browser trusts the page, so it runs the attacker’s code with the same privileges as the legitimate site, including access to cookies, session tokens, and the page’s data.

The root cause is almost always the same: an application takes input from a user and includes it in a page without properly encoding or sanitizing it. The browser cannot tell the difference between the developer’s script and the attacker’s, so it executes both.

XSS comes in three main forms. Stored XSS plants the script permanently on the server, in a comment or profile field, where it runs for every visitor who loads that content. Understanding stored XSS vs reflected XSS comes down to persistence: reflected XSS bounces the script off the server in a single response, usually through a crafted link, and only affects the person who clicks it. The third type behaves differently enough to deserve its own section.

Why DOM-Based XSS Slips Past Traditional Defenses

DOM based XSS happens entirely in the browser. Instead of the malicious input passing through the server and coming back in a response, client-side JavaScript reads attacker-controlled data (from the URL, for example) and writes it into the page’s DOM without sanitizing it first.

That distinction matters for defense. Server-side input filters never see the payload, because it may never leave the browser. A value placed after the # in a URL, for instance, is not even sent to the server. Any protection that lives on the backend is blind to it.

This is also why many automated tools struggle here. Catching DOM-based XSS means understanding how the application’s JavaScript actually handles data at runtime, not just inspecting requests and responses. Static checks and simple scanners tend to walk right past it.

What an Attacker Can Actually Do With XSS

Session hijacking is the headline risk. If the script can read a session cookie or token, the attacker can impersonate the victim and take over their account, no password required. Even where cookies are protected, the script can act on the user’s behalf inside the app while they are logged in.

The reach goes further than theft. XSS attack examples include keylogging login forms, injecting fake prompts to phish credentials, rewriting page content, and quietly performing actions as the victim, like changing an email address or moving money.

At the low end, XSS is used for defacement or spreading a self-propagating worm across a platform. At the high end, it chains into full account takeover and deeper compromise. Treating it as a cosmetic issue is how minor findings turn into breaches.

How to Prevent and Catch XSS Early

Output encoding is the core defense. Encode user-controlled data based on where it lands in the page (HTML body, attribute, JavaScript, URL) so the browser renders it as text instead of executing it. Modern frameworks encode by default, which helps, but developers routinely reintroduce the risk with raw HTML injection or unsafe DOM APIs.

Layer a Content Security Policy on top. A well-tuned CSP restricts where scripts can load from, limiting the damage even if an injection slips through. It is a safety net, not a substitute for encoding.

Finding XSS before attackers do takes testing that exercises real input handling, including the client-side logic where DOM-based flaws hide. Automated scanning helps, but its limits are worth understanding, which is why dynamic application security testing (DAST) is often paired with the deeper coverage of hands-on modern application penetration testing.


FAQ

Yes. Frameworks like React and Angular encode output by default, which cut the volume of basic XSS sharply. But developers still bypass those protections with functions that inject raw HTML, and DOM-based flaws live in custom JavaScript the framework does not guard. XSS remains near the top of real-world findings.

Absolutely. If an injected script can read a session token or perform authenticated actions, an attacker can impersonate the victim completely. Even with protected cookies, the script runs in the user’s session and can change account details, add credentials, or trigger sensitive operations, which often amounts to takeover in practice.

Not dependably. A web application firewall can block known payload patterns, but attackers routinely bypass filters with encoding and obfuscation, and a WAF cannot see DOM-based XSS that never reaches the server. Treat a firewall as one layer, not a fix. Output encoding in the code is the real defense.

XSS runs attacker-controlled script inside the victim’s browser, giving broad control over the page and session. CSRF tricks the victim’s browser into sending a forged, authenticated request without running any script. XSS is an injection flaw, CSRF abuses trust in existing sessions, and XSS can even be used to defeat CSRF defenses.