EXERCISE 3 — Prioritized XSS hardening plan for a React app ============================================================ CONTEXT: React app, cookie-based session, a rich-text comment feature. PRIORITIZED PLAN (foundation first, backstops last): 1. KEEP THE SAFE-BY-DEFAULT PATH; AUDIT THE ESCAPE HATCHES. (foundational) - React auto-escapes {value}, so ordinary output is already safe -- don't break that. Grep for dangerouslySetInnerHTML, plus raw innerHTML/ document.write/eval. The rich-text comment almost certainly uses dangerouslySetInnerHTML -> that's the prime suspect. 2. SANITIZE THE RICH-TEXT COMMENT HTML. (foundational -- the live hole) - Wherever comment HTML is rendered: dangerouslySetInnerHTML={{ __html: DOMPurify.sanitize(comment) }} with a narrow ALLOWED_TAGS/ATTR. Prefer Markdown rendered safely if the feature allows. Sanitize on output; keep DOMPurify updated (mXSS). 3. CONTEXT-CORRECT ENCODING / URL VALIDATION FOR NON-TEXT BINDINGS. - For any user-controlled href/src, allowlist the scheme (http/https/mailto), reject javascript:/data:. (Auto-escaping doesn't cover URL schemes.) Store raw, encode/validate at output. 4. COOKIE HARDENING. (mitigation) - Session cookie: HttpOnly + Secure + SameSite=Lax/Strict. HttpOnly blunts cookie THEFT (not session riding); SameSite also helps CSRF. This is mitigation, NOT an XSS fix. 5. STRICT CSP AS BACKSTOP. (defence in depth) - Nonce-based script-src, NO 'unsafe-inline'; object-src 'none'; base-uri 'self'. Roll out via Report-Only, fix violations, then enforce. - Add Trusted Types (require-trusted-types-for 'script') to structurally block raw-string DOM sinks -- routing them through a DOMPurify policy. 6. INPUT VALIDATION ON STRICT FIELDS + TESTING. (defence in depth / verify) - Allowlist-validate structured fields (data quality). Then TEST: input->output map, context probes, DOM source->sink review, scanners + manual review of the hatches. JUSTIFYING THE ORDERING (foundational vs backstop): - OUTPUT ENCODING / SANITIZATION (steps 1-3) are FOUNDATIONAL: they actually PREVENT the injection from becoming executable. Without them, nothing else truly fixes the bug. The live comment hole (2) is highest priority because it's stored XSS hitting every viewer. - COOKIE FLAGS (4) only reduce impact of a successful XSS -- mitigation, so after the real fixes. - CSP + TRUSTED TYPES (5) are BACKSTOPS: they limit blast radius / block sinks IF prevention fails. Valuable, but must not be relied on alone (a single 'unsafe-inline' guts CSP), so they layer on top of the foundation, not instead of it. - VALIDATION/TESTING (6) is quality + verification, last. WHY FIXING XSS ALSO PROTECTS THE APP'S CSRF DEFENCES: - The app uses COOKIE sessions, so it relies on CSRF defences (tokens / SameSite). But an XSS runs SAME-ORIGIN and can READ the anti-CSRF token from the page and submit a valid request (session riding, Chapter 5 / CSRF course) -- defeating those defences entirely. - So as long as XSS exists, the CSRF protection is bypassable. Eliminating XSS removes the attacker's ability to read the token and forge requests, restoring the CSRF defences' effectiveness. This is the concrete reason "XSS is strictly more powerful than CSRF -- FIX XSS FIRST." Hardening XSS is, in part, hardening CSRF.