Stored XSS
Stored XSS occurs when an application saves attacker-controlled input and later includes it in responses served to other users without encoding. The payload is written once and executed by every user whose browser receives the response containing it.
Storage Locations
Any field the application persists and later renders is a potential injection point:
- User profile fields: display name, biography, location, website URL
- User-generated content: comments, forum posts, reviews, messages
- Uploaded file names and metadata displayed in the UI
- Support ticket content rendered in an admin interface
- Log entries or event descriptions displayed in dashboards
The storage medium is irrelevant. Payloads stored in relational databases, document stores, caches, or log files all execute the same way when rendered without encoding.
Stored vs. Reflected: Reach and Targeting
Reflected XSS requires delivering a crafted URL to each victim. Stored XSS executes against every user who views the affected page without any additional action from the attacker. A comment containing a payload on a high-traffic page executes against all visitors. A payload in a user's display name executes wherever that name appears across the application.
Stored XSS does not require social engineering at scale. Once placed, the payload operates autonomously until removed.
The Source-Sink Gap
A common source of stored XSS is the gap between where data enters the system and where it is rendered. Input arrives at an API endpoint or form handler. It is stored in a database. It is retrieved and rendered by a template weeks or months later, written by a different developer who does not know the field contains unencoded user input.
This is why input encoding at write time is not sufficient as a defense on its own. If data is stored raw, any code path that reads and renders it must also encode it. Missing one rendering location is sufficient for exploitation.
Privilege Escalation via Admin Interfaces
Admin interfaces often display user-generated content for moderation: support tickets, reported content, user profile data. If the admin interface renders this content without encoding, a user with no special privileges can inject a payload that executes in the administrator's browser session.
The administrator's session has higher privileges than the attacker's account. Actions possible with that session, such as account privilege changes, user deletion, or configuration changes, become available to the attacker through the injected script. This makes stored XSS in admin-visible fields a privilege escalation path.
Removal
Removing a stored XSS payload requires identifying and deleting or sanitizing the stored data, not just patching the rendering code. Patching the renderer prevents future execution but does not eliminate the stored payload. If a second rendering path exists, the payload may still execute through that path.