Back to Security Academy
INJECTION DEFENSESINTERMEDIATE7 min readCWE-79ZX-XSS-001

Stored & Reflected Cross-Site Scripting (XSS) in Modern React & Next.js

React escapes strings in JSX, but dangerouslySetInnerHTML, javascript: URLs, and unescaped SSR hydration payloads still introduce critical XSS.

Why AI coding tools introduce this vulnerability

AI tools building markdown rendering or rich text components routinely output <div dangerouslySetInnerHTML={{ __html: userContent }} /> without sanitizing the HTML with DOMPurify. They also render user links like <a href={userLink}>, allowing javascript:alert(1) payloads.

Technical Vulnerability Analysis

Cross-Site Scripting allows attackers to inject malicious JavaScript into victim browsers. In modern single-page applications, XSS can steal session tokens from localStorage/cookies, hijack the DOM to display fraudulent credential prompts, or execute transactions on behalf of the victim.

VULNERABLE IMPLEMENTATION (DO NOT USE)
// AI-Generated Markdown/Blog Component (VULNERABLE)
export function UserBio({ bioHtml, websiteUrl }: { bioHtml: string; websiteUrl: string }) {
  return (
    <div>
      {/* 1. Unsanitized HTML insertion */}
      <div dangerouslySetInnerHTML={{ __html: bioHtml }} />
      
      {/* 2. Unsanitized link href allows javascript: protocol */}
      <a href={websiteUrl}>Visit Website</a>
    </div>
  );
}
dangerouslySetInnerHTML and javascript: links execute attacker code in the victim browser.

Remediation Playbook & Code Fixes

SECURE HARDENED PATCH (src/components/UserBio.tsx)
import DOMPurify from 'isomorphic-dompurify';

export function UserBio({ bioHtml, websiteUrl }: { bioHtml: string; websiteUrl: string }) {
  // 1. Sanitize HTML tags and strip event handlers
  const cleanHtml = DOMPurify.sanitize(bioHtml);

  // 2. Validate URL protocol is strictly http or https
  const safeHref =
    websiteUrl.startsWith('http://') || websiteUrl.startsWith('https://')
      ? websiteUrl
      : '#';

  return (
    <div>
      <div dangerouslySetInnerHTML={{ __html: cleanHtml }} />
      <a href={safeHref} rel="noopener noreferrer">Visit Website</a>
    </div>
  );
}

Why this fix works: Sanitize HTML with DOMPurify and strictly enforce http:// or https:// URL protocols.

Local CLI Verification Commands

Verify injected script tags are stripped from server rendered response.
curl -s 'https://example.com/profile?bio=<script>alert(1)</script>' | grep '<script>alert(1)</script>'
Expected result: (empty response - payload must be sanitized or encoded)

Pre-Deployment Self-Audit Checklist

Knowledge Check: Verify Your Understanding

What is the primary architectural cause of stored & reflected cross-site scripting (xss) in modern react & next.js?

Test your website for ZX-XSS-001

Run our 14 deterministic engines to inspect your live deployment.

Launch scan for this flaw