C-SWCM study materials
C-SWCM study resources

Stored XSS in a Rich-Text Comment Field

C-SWCM · Put your knowledge to work in context.

All case studies

Stored XSS in a Rich-Text Comment Field

A community site lets users post comments with basic formatting (bold, links, lists). The frontend uses a rich-text editor that produces HTML, and the server stores the HTML and renders it directly into the page. The team added an output-encoding library that escapes all HTML on render, but users complained that formatting disappeared, so an engineer changed the renderer to only escape <script> tags. Since then, moderators report odd pop-ups and redirects. The site has a strict CSP in report-only mode, and the security team wants to move to enforcement. The comment field is used by 200,000 users, and the team cannot rewrite the editor before the next release in two weeks.

1. Why is escaping only `<script>` tags insufficient? Give concrete payload classes that would still work and explain the correct defense.

Read the worked answer

Blocklisting the <script> tag ignores many other execution vectors: event-handler attributes such as <img src=x onerror=...>, <svg onload=...>, javascript: URLs in links, <iframe srcdoc=...>, CSS-based attacks, and mutation XSS where the browser reparses malformed markup differently than the sanitizer. The correct defense is contextual output encoding plus allowlist-based sanitization of the stored HTML. Because the field intentionally allows some HTML, use a well-maintained HTML sanitizer configured with an allowlist of tags and attributes, and sanitize on output (or on input and again on output). Do not rely on regex or tag blocklists. Also encode any user data inserted into attributes, URLs, or JavaScript contexts. This preserves formatting while removing dangerous constructs.

2. How should the team use CSP during the two-week window, and what should enforcement look like?

Read the worked answer

Keep CSP in report-only mode while collecting violation reports, but do not treat reporting as a fix; it is detection. Add a nonce- or hash-based script-src and avoid unsafe-inline. During the window, deploy the sanitizer first, then tighten CSP in stages: start with script-src 'self' 'nonce-...', keep object-src 'none', base-uri 'none', and frame-ancestors 'none'. Monitor reports for legitimate breakage, then enforce. CSP is defense in depth and will not stop DOM-based sinks or sanitizer bypasses alone, so it must accompany the sanitizer fix. Document a rollback plan and test with representative payloads in staging.