◇ Web Security

Cross-Site Scripting (XSS): Types, Attacks, and Defenses

Cross-site scripting, commonly abbreviated XSS, is a web vulnerability that lets an attacker execute malicious scripts in another user's browser in the context of a trusted site. Because the script runs with the victim's privileges, it can steal session tokens, perform actions on the user's behalf, capture keystrokes, or rewrite page content. XSS remains one of the most prevalent web weaknesses, and understanding its variants is essential for building safe front-end and back-end code.

How Cross-Site Scripting Works

At its core, XSS happens when an application takes data that an attacker controls and places it into an HTML page without proper encoding or sanitization. The browser cannot tell the difference between script the developer intended and script an attacker injected, so it executes both. The injected code runs within the site's origin, which means it can reach cookies (unless protected), the DOM, and any data the user can see.

The attacker's goal is usually to get JavaScript to run. That might mean injecting a <script> tag, an event handler such as onerror, or a javascript: URL, depending on where the input lands in the page.

Impact of a Successful XSS Attack

Because injected script executes with the same authority as the page itself, the consequences reach well beyond a defaced page. An attacker can read and exfiltrate data rendered on the page, submit forms as the victim, and pivot to other actions the user is authorized to perform. If the site stores authentication material somewhere reachable by script, the attacker can hijack the session and impersonate the user entirely. This is why XSS is treated as a high-severity flaw rather than a cosmetic bug, and why the payload location matters as much as its content.

Types of XSS

Reflected XSS

In reflected XSS, the malicious input is included in a response immediately, typically through a URL parameter or form field. The victim must be lured into clicking a crafted link. Because the payload is not stored, each attack targets a specific user who follows the link.

Stored XSS

Stored, or persistent, XSS occurs when the malicious input is saved by the application, for example in a comment, profile field, or support ticket, and later served to other users. This is more dangerous because it can affect every visitor who views the poisoned content without any special link.

DOM-Based XSS

DOM-based XSS lives entirely in client-side code. It happens when JavaScript reads attacker-controllable data, such as the URL fragment, and writes it into a dangerous sink like innerHTML or document.write. The server may never see the payload, so server-side filtering alone cannot catch it.

Defending Against XSS

No single control stops every case, so layered defenses are recommended.

Context-Aware Output Encoding

The primary defense is to encode untrusted data for the exact context where it is rendered. HTML body, HTML attributes, JavaScript, CSS, and URLs each require different escaping. Most templating engines encode HTML by default, which prevents the majority of reflected and stored XSS when used consistently.

Safe DOM APIs

Prefer APIs that treat input as text rather than markup. Assigning to textContent is safe, whereas assigning untrusted data to innerHTML is not.

// Vulnerable: untrusted input is parsed as HTML
element.innerHTML = location.hash.slice(1);

// Safer: input is inserted as plain text, not markup
element.textContent = location.hash.slice(1);

Input Validation and Sanitization

Validate input against expected formats, and when you must allow rich HTML, run it through a well-maintained sanitizer that removes scripts and dangerous attributes. Never build your own blocklist of tags; attackers know countless bypasses.

Content Security Policy

A strong Content Security Policy acts as a safety net by restricting which scripts the browser will execute, so even an injected payload may fail to run. Combine it with nonces or hashes and avoid unsafe-inline.

Protecting Session Data

Mark session cookies as HttpOnly so injected scripts cannot read them directly, as covered in secure cookies. This does not stop XSS but limits token theft. Note that XSS and CSRF are distinct issues, and defenses for one do not fully cover the other.

Framework Features and Trusted Types

Web frameworks provide automatic escaping and opt-in protections such as Trusted Types, which force dangerous DOM sinks to accept only vetted values. Enabling these features closes many DOM-based gaps.

A Practical Prevention Checklist

  1. Encode output based on its rendering context, and rely on your framework's auto-escaping.
  2. Avoid dangerous sinks like innerHTML, eval, and document.write with untrusted data.
  3. Sanitize any user-supplied HTML with a trusted library.
  4. Deploy a restrictive Content Security Policy with nonces or hashes.
  5. Set HttpOnly and other protective attributes on session cookies.
  6. Test continuously with both automated scanners and manual review in authorized environments.

Key Takeaways

  • Cross-site scripting executes attacker-controlled scripts in a victim's browser within a trusted site's origin.
  • The three main types are reflected, stored, and DOM-based, and DOM-based XSS requires client-side fixes.
  • Context-aware output encoding is the primary defense, backed by safe DOM APIs and input sanitization.
  • A strong Content Security Policy and HttpOnly cookies reduce impact when other controls fail.
  • Layered defenses and continuous authorized testing provide the most reliable protection.
xssweb-securityjavascriptinput-validationappsec

Frequently asked questions

What is cross-site scripting (XSS)?

Cross-site scripting is a web vulnerability where an attacker injects malicious JavaScript into a page that other users view, causing it to run in their browsers within the trusted site's origin. Because the script runs with the victim's privileges, it can steal session tokens, perform actions as the user, capture keystrokes, or rewrite page content. It remains one of the most common and high-impact web application flaws.

How does cross-site scripting work?

XSS happens when an application places attacker-controlled data into an HTML page without proper encoding or sanitization, so the browser executes the injected code as if the developer had written it. The payload might be a script tag, an event handler such as onerror, or a malicious URL scheme, depending on where the input lands. The injected script runs in the site's origin, giving it access to cookies, the DOM, and visible data.

What are the types of cross-site scripting?

The three main types are reflected, stored, and DOM-based XSS. Reflected XSS echoes malicious input from a request straight back in the response, requiring the victim to click a crafted link, while stored XSS saves the payload on the server so it affects everyone who views the poisoned content. DOM-based XSS occurs entirely in client-side JavaScript that writes untrusted data into a dangerous sink such as innerHTML.

How do you prevent cross-site scripting?

Prevent XSS with context-aware output encoding for the exact place data is rendered, treating all user input as untrusted and relying on framework auto-escaping. Prefer safe DOM APIs like textContent over innerHTML, sanitize any user-supplied HTML with a trusted library, and deploy a strong Content Security Policy as a safety net. Layering these defenses catches cases that any single control would miss.

What is the difference between stored and reflected XSS?

Reflected XSS is delivered in a single request and immediately echoed back, so it targets a specific victim who must follow a crafted link and is not persisted. Stored XSS is saved by the application and served to many users later without any special link, making it generally more dangerous because it can affect every visitor to the poisoned page. Both execute attacker script in the browser, so both require proper output encoding to prevent.

Why is cross-site scripting dangerous?

XSS is dangerous because injected script runs with the same authority as the page, so it can read and exfiltrate on-screen data, submit forms as the victim, and hijack the session if it can reach authentication tokens. This lets an attacker impersonate the user entirely rather than merely deface a page. Marking session cookies HttpOnly limits token theft but does not stop the underlying script execution.

Try it hands-on

K0G is an open toolkit of browser-based security utilities — hashing, encoding, JWT, certificates, crypto and more, all running locally in your browser.

Explore the tools →