Overview
Stored Cross-Site Scripting (Stored XSS) occurs when a web application accepts user-controlled input, persists it to a back-end data store (database, log file, message queue, etc.) without adequate sanitisation, and later renders that data in an HTTP response without proper output encoding. Unlike Reflected XSS—where the malicious payload is embedded in a URL and executed in a single round trip—Stored XSS payloads are durable: every user whose browser renders the affected page executes the injected script, often without any interaction beyond visiting the page.
Stored XSS is classified under CWE-79 (Improper Neutralization of Input During Web Page Generation) and maps to OWASP Top 10 A03:2021 – Injection. Because the attack is self-propagating and does not require tricking a victim into clicking a crafted link, it is generally considered more severe than Reflected XSS.
How it works
The attack life cycle has three distinct phases:
- Injection: The attacker submits a payload—typically a
<script>tag, an event-handler attribute, or an encoded variant—through any input surface the application accepts (comment fields, user profiles, product reviews, file names, HTTP headers stored in logs, etc.). - Persistence: The application stores the unsanitised payload in a data store. From this point the payload is latent and ready to execute for any future visitor.
- Execution: When a legitimate user (or administrator) requests the page containing the stored data, the server includes the payload in the HTML response. The browser, unable to distinguish the injected script from legitimate application code, executes it in the context of the origin.
Key technical characteristics:
- Execution occurs in the victim's browser under the application's origin, granting the script access to cookies (including session tokens), localStorage, sessionStorage, and the DOM.
- Payloads can exfiltrate session cookies to an attacker-controlled server, perform actions on behalf of the victim (CSRF-equivalent), redirect users to phishing pages, or serve drive-by downloads.
- Polyglot payloads and encoding tricks (HTML entities, Unicode escapes, base64, SVG vectors, CSS expressions) are commonly used to bypass naive input filters.
- Second-order (or second-stage) XSS is a variant where stored data is retrieved and unsafely interpolated into a later, different page or context—often a maintenance or admin interface—making it harder to discover during standard testing.
- The
HttpOnlycookie flag prevents JavaScript from reading cookies directly, but does not prevent all classes of session hijacking achievable through XSS (e.g., forging authenticated requests).
Business impact
Because Stored XSS executes automatically for every affected visitor, the blast radius scales with the traffic to the compromised page. Documented consequences include:
- Account takeover: Session tokens captured via
document.cookieexfiltration allow attackers to impersonate victims without knowing their passwords. - Credential harvesting: Injected scripts can overwrite login forms or inject fake authentication dialogs to harvest plaintext credentials.
- Privilege escalation: If an administrator views an infected page, the attacker gains administrative-level access, potentially compromising the entire application.
- Malware distribution: Scripts can silently redirect users to exploit kits or trigger drive-by downloads.
- Regulatory exposure: Unauthorised access to personal data can trigger breach notifications and fines under GDPR, HIPAA, PCI DSS, and similar frameworks.
- Reputational damage: Publicly known XSS incidents erode user trust, particularly for e-commerce, banking, and healthcare platforms.
- Data integrity compromise: Scripts can alter on-screen content, leading users to make decisions based on falsified information (e.g., manipulating financial figures).
How to fix it
Defence requires controls at multiple layers. No single measure is sufficient on its own.
- Context-aware output encoding: Encode all user-supplied data at the point of rendering, not at the point of storage. Use the appropriate encoding for the output context: HTML entity encoding for HTML body/attributes, JavaScript string escaping for inline scripts, URL encoding for href/src attributes, and CSS escaping for style contexts. Libraries such as OWASP Java Encoder, Microsoft AntiXSS, or framework-native templating engines (React's JSX, Angular's interpolation, Django's auto-escaping) apply this automatically when used correctly.
- Input validation and sanitisation: Reject or sanitise input that does not conform to expected formats. For fields that must accept rich text (HTML), use a well-maintained allowlist-based HTML sanitisation library (e.g., DOMPurify for client-side, bleach for Python, HtmlSanitizer for .NET). Blocklist approaches are insufficient against encoding bypass techniques.
- Content Security Policy (CSP): Deploy a strict CSP header (
Content-Security-Policy) that disallows inline scripts ('unsafe-inline') and restricts script sources to trusted origins. A policy using nonces or hashes for legitimate inline scripts provides the strongest protection. CSP is a defence-in-depth control and should not replace server-side fixes. - HttpOnly and Secure cookie flags: Set
HttpOnlyon all session cookies to prevent JavaScript access. SetSecureto enforce HTTPS-only transmission. - Subresource Integrity (SRI): For externally loaded scripts, use SRI hashes to detect tampering.
- Trusted Types API: In modern browsers, enforce the Trusted Types API (
require-trusted-types-for 'script') to block DOM injection sinks from accepting raw strings. - Security code review and DAST: Include XSS test cases in static analysis (SAST) pipelines and perform dynamic application security testing (DAST) against all input surfaces. Sensagraph automatically detects Stored XSS vulnerabilities during continuous scanning.
- Web Application Firewall (WAF): A WAF can provide a secondary layer of detection and blocking for known XSS patterns, but should not be the primary control.
References
- OWASP Top 10 A03:2021 – Injection
- OWASP – Cross-Site Scripting (XSS)
- OWASP XSS Prevention Cheat Sheet
- OWASP DOM-based XSS Prevention Cheat Sheet
- MITRE CWE-79 – Improper Neutralization of Input During Web Page Generation
- MDN – Content Security Policy (CSP)
- W3C – Content Security Policy Level 3
- MDN – Trusted Types API
- DOMPurify – Trusted HTML Sanitisation Library