Cross Site Scripting (Persistent)
ID |
cross_site_scripting_persistent |
Severity |
critical |
Kind |
Cross-Site Scripting |
CWE |
79 |
Description
Stored Cross-Site Scripting (also known as Persistent or Second-Order XSS) is a vulnerability that arises when an application receives data from an untrusted source, persists it on the server — typically in a database, message forum, comment field, or visitor log — and later includes that unsanitized data within HTTP responses served to other users. Unlike Reflected XSS, the victim does not need to click a specially crafted link or visit a malicious page; simply viewing a web page that renders the stored payload is enough to trigger the attack. This makes Stored XSS the most dangerous variant of Cross-Site Scripting, because a single injected payload can automatically execute in the browsers of every user who accesses the affected content, enabling large-scale compromise without any direct interaction between the attacker and the victims.
Rationale
An attacker exploits Stored XSS by submitting malicious script content — through form fields, profile data, file uploads, or API parameters — that the application saves and subsequently renders to other users without proper encoding or sanitization. Common exploitation vectors include posting a JavaScript payload in a forum comment, embedding it in a user profile field, or injecting it into a support ticket that will be viewed by administrators, thereby enabling privilege escalation. A successful exploit can allow the attacker to hijack user sessions through cookie theft, perform actions on behalf of authenticated users, redirect victims to phishing sites, deface the application, exfiltrate sensitive data via API requests made from the victim’s browser, or propagate self-replicating XSS worms — and because the payload is served from the trusted application domain, browser security mechanisms such as the same-origin policy offer no protection.
Remediation
Apply context-aware output encoding on all dynamic content. Every time user-supplied data is rendered in an HTML page, apply the encoding appropriate for the output context — HTML entity encoding for element content, attribute encoding for HTML attributes, JavaScript encoding for inline scripts, and URL encoding for URL parameters. Use a vetted library or framework such as the OWASP ESAPI Encoding module, Microsoft’s Anti-XSS library, or built-in template engine auto-escaping to perform this encoding consistently.
Validate and sanitize all input on the server side. Use an "accept known good" strategy with an allow-list of acceptable values that strictly conform to specifications. Reject or transform any input that does not match. Do not rely solely on deny-lists or on client-side validation, as attackers can bypass client-side checks by modifying requests directly.
Adopt a Content Security Policy (CSP). Deploy a strict CSP header that disallows inline scripts (script-src 'self') and restricts script sources to trusted origins. While CSP does not fix the root cause, it acts as a defense-in-depth layer that can prevent or significantly limit the execution of injected scripts.
Set the HttpOnly and Secure flags on session cookies. Mark session cookies as HttpOnly to prevent client-side scripts from accessing them via document.cookie, and as Secure to ensure they are only transmitted over HTTPS. This limits the impact of a successful XSS exploit by making session hijacking more difficult.
Specify a character encoding for every page. Declare a character encoding such as UTF-8 in the HTTP Content-Type header and in the HTML <meta> tag. When no encoding is specified, the browser may guess incorrectly, treating certain byte sequences as special characters and opening up the client to subtle XSS attacks.
References
-
Cross-Site Scripting, in OWASP Community.