A website that accepts comments, profile information, messages, or formatted text has to decide how to display that content later. Stored cross-site scripting, usually shortened to stored XSS, occurs when saved, attacker-controlled content is rendered in a way that executes code in another user’s browser.
This is relevant to public websites, online stores, SaaS products, and internal web applications. The exposed page might be a public review or a private administration screen. What matters is who can submit the content, where it appears, and what the browser allows the injected code to do.
What makes stored XSS different?
With stored XSS, the application retains the dangerous content and serves it again. A person may encounter it through normal use of the site, without opening a specially crafted link. The input and the affected page can be in different parts of the application.
OWASP’s XSS overview distinguishes stored attacks from reflected attacks, where supplied input is included in an immediate response. XSS can also result from unsafe browser-side handling of data. These descriptions concern different parts of the problem: content can be stored on the server and later inserted unsafely by frontend code.
For a business owner, the useful question is where submitted content becomes something another person views. Ask the development team to trace that path across the customer interface and staff tools. A successful check of the submission form does not establish that every page displaying its contents handles them safely.
Include the pages your team uses
An assessment should inventory saved fields and their display locations. Candidate inputs include profile names, reviews, support messages, filenames, and imported descriptions. Their presence does not establish a vulnerability; each is a place to investigate how the application treats untrusted data.
Include staff search results, moderation screens, previews, and record detail pages in the coverage list. A field can appear as plain text in one component and formatted content in another. Testing only the customer-facing page leaves those other renderers unexamined.
Give the assessor the roles needed to inspect the agreed paths. Record which role submits the data and which role opens it. If the staff application is excluded, say so in the report. A public website scan cannot provide evidence about an authenticated screen it never reached.
Prioritize fields that cross from a less trusted user to a more privileged viewer. Establish the actual path before assigning severity. A theoretical administrator impact is not a confirmed administrator impact if the content never reaches an administrator’s browser.
Plain text and rich text need different handling
For plain text, the browser should display the characters as content. Context-appropriate output encoding and safe rendering APIs help prevent those characters from being interpreted as markup or code. The required handling depends on whether the value appears in page text, an attribute, a URL, or another context.
Rich text intentionally allows some markup. It needs a maintained HTML sanitizer with a policy defining the permitted elements and attributes. Removing one suspicious word or relying on a short list of forbidden strings is not an adequate substitute.
OWASP’s XSS prevention guidance explains these distinctions and the limits of framework protections. Automatic escaping helps, but code that deliberately inserts raw HTML can bypass that protection. Review those exceptions and any transformation applied after sanitization.
The engineering review should identify the component responsible for rendering each affected field. That makes the fix easier to assess and gives the team a concrete place to add regression coverage.
What HttpOnly and CSP can protect
An HttpOnly cookie cannot be read directly through browser JavaScript. However, the browser can still attach it to requests initiated by JavaScript. MDN’s Set-Cookie reference documents that distinction. Preventing direct cookie theft does not remove the consequences of code executing within the application’s origin.
Content Security Policy can restrict script execution and some destinations the page may contact. Its effect depends on the enforced policy and the particular injection path. A report-only policy records violations without blocking them. MDN’s CSP guide explains the enforcement model.
Keep these controls in the assessment, but also examine the unsafe rendering itself. Record whether the browser blocked execution and why. Do not describe an injection as executed XSS if the evidence shows only that text reached the page.
What a useful stored XSS test should establish
Start with a harmless marker to map where a saved value appears. Then use an agreed, non-destructive execution check in designated test records. Testing should avoid collecting real session data, reading other customers’ records, or leaving active content for unrelated users to encounter.
The evidence should connect the submitted field, stored record, affected page, viewer role, and browser result. Capture the relevant application version and security policy. A screenshot of an alert alone leaves too much unexplained: it does not show who could submit the input or whether a second user could encounter it.
Distinguish content that executes only for its own author from content that reaches other users. Also separate confirmed capabilities from possible follow-on effects. Showing script execution does not automatically prove access to every customer account or control of the underlying server.
Agree cleanup before testing begins. Remove the test content and confirm it no longer appears in the relevant views. Where a safe production test cannot be arranged, use a representative environment and document any differences that limit the result.
Verify the fix on existing records too
A patch may change new submissions while leaving previously stored content available. Retest a designated record created before the fix, as well as one created after it. Check every affected display component identified during the assessment.
Preserve legitimate functionality during the retest. Plain-text fields should still display ordinary punctuation correctly. Rich-text fields should retain the formatting the product promises while rejecting unsafe constructs. A fix that silently damages customers’ saved content creates another problem for the team to resolve.
Ask for a short record of the original behavior, the rendering change, and the result under the deployed policy. Use Ceron’s security assessment retesting guide to define closure evidence.
If your application stores user content, include its display paths in a customer portal security assessment. Discuss the relevant pages and test accounts with Ceron so the scope reflects how people actually use the application.