Blind Stored XSS
Map delayed browser rendering, distinguish passive HTML fetches from JavaScript execution, and prove only a callback with no victim data collection.
Every step requires authorization. Review side effects before running commands against a live system.
Define the boundary before the test.
- Preconditions
- Current scope, observed technology, reachable feature, controlled identities or data, and a recovery plan.
- Expected evidence
- One observable response, state, or callback that distinguishes the hypothesis from normal behavior.
- Negative control
- The same action with the security-sensitive variable removed, replaced, or bound to its rightful owner.
- False-positive checks
- Cache, retry, race, reflection, proxy rewriting, stale sessions, and unrelated asynchronous activity.
- Chain gate
- Record the proven primitive and the next missing prerequisite. Do not expand impact until scope and authorization still permit the next step.
Lock the human-impact boundary
Confirm the program permits blind XSS testing and identify fields likely to reach staff, moderators, support agents, administrators, email clients, exports, or dashboards. Use a controlled record when possible and stop if an uncontrolled person is likely to be exposed.
Map source, storage, recall, and sink
Give every candidate field its own case ID. Record how it enters, where it may be stored, which workflow recalls it, the likely rendering context, and who can view it. Include body fields, filenames, profile values, ticket metadata, imported records, and selected headers only when policy permits.
CASE=bxss-017 FIELD=ticket-subject REQUEST=req-42Plant an inert canary first
Submit a plain alphanumeric marker before markup. Recall it through your own account or test object and record every transformation. A stored marker proves persistence, not HTML interpretation or script execution.
BXSS017TICKETSUBJECTREQ42Separate render from execution
If markup testing is allowed, use a no-referrer image callback to test rendering. Only then use a callback-only event payload to test JavaScript execution. Use different unique hosts or paths for render and execution so an image request cannot be mislabeled as XSS.
<img referrerpolicy=no-referrer src=https://bxss-017-render.UNIQUE.oast.example/pixel>Correlate delayed interactions
Preserve submission time, record ID, insertion point, callback token, protocol, source address, user agent, request path, and delay. Keep one generated token unsubmitted. A DNS lookup alone is weaker than an HTTP request, and an HTTP image fetch still does not prove JavaScript execution.
interactsh-client -server YOUR_PRIVATE_INTERACTSH_SERVER -json -o bxss-interactions.jsonlRepeat with controls and remove the record
Repeat once with a fresh token in the same field and once with the sensitive delimiter encoded or removed. Delete or neutralize the controlled record after sufficient proof. Report the smallest supported claim and label the viewer role as observed, inferred, or unknown.
Common guidance
- Callback JavaScript must not read cookies, storage, credentials, forms, page content, page URL, or referrer data.
- A scanner, preview service, mail security gateway, or server-side renderer can fetch an image without a human browser executing JavaScript.
- Do not spray staff-facing fields. One uniquely correlated controlled record is stronger evidence than many ambiguous callbacks.
- Keep render, execution, and repeat-control tokens separate. Their paths should explain what each interaction can prove.
Mapped workflow tools
References
- https://owasp.org/www-project-web-security-testing-guide/stable/4-Web_Application_Security_Testing/07-Input_Validation_Testing/02-Testing_for_Stored_Cross_Site_Scripting ↗
- https://owasp.org/www-project-web-security-testing-guide/latest/4-Web_Application_Security_Testing/07-Input_Validation_Testing/14-Testing_for_Incubated_Vulnerability ↗
- https://portswigger.net/kb/issues/00200100_cross-site-scripting-stored ↗
- https://portswigger.net/web-security/cross-site-scripting/contexts ↗
Version history: normalized permanent page created 2026-08-20.