What Is Cross-Site Scripting and How Does It Work?
Cross-Site Scripting (XSS) is a widespread web security vulnerability that allows attackers to inject malicious scripts into trusted websites viewed by other users. This article explains the fundamentals of XSS, outlines its primary variants, and details the technical mechanisms attackers use to hijack client-side JavaScript runtimes, access sensitive session data, and manipulate web applications from within the victim’s browser.
Understanding Cross-Site Scripting (XSS)
Cross-Site Scripting occurs when an application includes untrusted user input in a web page without proper validation or escaping. Because the browser cannot distinguish between legitimate application scripts and malicious injections, it executes the payload within the context of the user’s active session.
XSS attacks generally fall into three main categories:
- Stored XSS (Persistent): The malicious script is permanently stored on the target server (such as in a database, message forum, or comment field). Victims execute the script automatically whenever they load the affected data.
- Reflected XSS (Non-Persistent): The payload is delivered via an immediate request-response cycle, typically embedded within a crafted URL parameter or search query that the server echoes back to the user.
- DOM-based XSS: The vulnerability exists entirely
within client-side code, where JavaScript reads input from a
controllable source (such as
window.location) and writes it to an unsafe sink (such asinnerHTMLordocument.write) without adequate sanitization.
How Attacker Scripts Compromise JavaScript Runtimes
When malicious code runs inside a browser’s JavaScript runtime, it operates under the Same-Origin Policy (SOP) permissions granted to the host domain. This allows the attacker to compromise the environment in several direct ways:
1. Data Exfiltration via Storage Access
The script can access all unsegmented data stored in client-side
storage mechanisms, including localStorage,
sessionStorage, and readable cookies (those not marked with
the HttpOnly flag). Attackers can capture authentication
tokens, API keys, and session identifiers, transmitting them directly to
a remote command-and-control server.
2. Monkey-Patching Native APIs
Because JavaScript is a dynamic, prototype-based language, an
injected script can overwrite core runtime objects and browser APIs. An
attacker can redefine functions such as fetch() or
XMLHttpRequest.prototype.send to intercept credentials and
request bodies before forwarding the real request, effectively creating
a silent man-in-the-browser keylogger or data sniffer.
3. DOM Manipulation and Interface Spoofing
The script has full access to the Document Object Model (DOM).
Attackers can dynamically rewrite forms, inject fake login overlays,
capture keystrokes via addEventListener('keypress', ...),
and redirect buttons to external malicious domains.
4. Performing Unauthorized Actions (CSRF Amplification)
Operating from within the authenticated runtime, the malicious payload can invoke internal application functions and make authenticated API calls on behalf of the victim. This allows the script to change passwords, transfer funds, or modify user settings without requiring the user’s explicit consent.
Key Defenses Against XSS
Preventing runtime compromise requires a multi-layered defense strategy:
- Context-Aware Output Encoding: Encode all dynamic data before inserting it into HTML, JavaScript, attributes, or CSS contexts.
- Safe API Usage: Prefer safe DOM manipulation
methods such as
textContentandcreateElementover sinks likeinnerHTMLoreval(). - Content Security Policy (CSP): Deploy strict CSP HTTP headers to restrict script execution sources, disable inline script execution, and prevent unauthorized network requests.
- Secure Cookies: Use the
HttpOnlyflag on sensitive session cookies to ensure they remain inaccessible to the JavaScript runtime.