Skip to content

Feat react security cheat sheet#2196

Open
anuragnedunuri wants to merge 2 commits into
OWASP:masterfrom
anuragnedunuri:feat-react-security-cheat-sheet
Open

Feat react security cheat sheet#2196
anuragnedunuri wants to merge 2 commits into
OWASP:masterfrom
anuragnedunuri:feat-react-security-cheat-sheet

Conversation

@anuragnedunuri

Copy link
Copy Markdown

Adds the React Security Cheat Sheet as discussed in issue #543 .

React's security model has several framework-specific patterns - dangerouslySetInnerHTML, prop injection via spread operators, Server Action authorization, RSC data boundary leakage, and AI agent DOM exposure among others. This cheat sheet documents these patterns with CWE mappings and practical architectural guidance for developers.

Covers seven areas: XSS prevention, sensitive data exposure, authentication and authorization, SSR security, dependency and supply chain security, and AI and emerging threats.

Passes markdown-link-check and markdownlint with zero errors.

Scope and sourcing

  • This PR is focused: it modifies a single cheat sheet and the scope is described in the PR body.
  • Every technical claim, recommendation, or threat assertion added in this PR is supported by a primary source linked inline.
  • I have read each source I cite and confirm it actually supports the claim. I have not relied on summaries, hearsay, or
    model-generated citations.

AI Tool Usage Disclosure

  • I have used AI tools to generate the contents of this PR. I have verified the contents and I affirm the results. The LLM used is Claude Sonnet 4.5 (claude.ai) and the approach was an iterative research and drafting process around proper CWE mappings and verification of all technical claims against primary sources. I have independently verified every citation and technical claim against the cited source.

This PR fixes issue #543

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds a new draft React Security Cheat Sheet documenting React- and Next.js-specific security pitfalls (XSS, sensitive data exposure, authz/authn, SSR/RSC risks, supply chain, and AI-related threats), with CWE mappings and references.

Changes:

  • Introduces a new cheatsheets_draft/React_Security_Cheat_Sheet.md document covering common React security sinks and mitigation patterns.
  • Includes example snippets for common anti-patterns (e.g., dangerouslySetInnerHTML, spread-prop injection, SSR JSON serialization, Server Actions) and safer alternatives.
  • Adds a references section linking to relevant OWASP cheat sheets and related resources.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.


Cross-Site Scripting (XSS) occurs when an attacker injects malicious scripts into a web page that are then executed in another user's browser. React escapes dynamic content rendered through JSX by default, converting special characters such as `<`, `>`, and `"` into their HTML-encoded equivalents so the browser treats them as text rather than executable code. This protection applies only to content rendered through JSX. Several React patterns bypass this protection entirely and must be handled with care.

Related CWE(Common Weakness Enumeration): [CWE-79: Improper Neutralization of Input During Web Page Generation (XSS)](https://cwe.mitre.org/data/definitions/79.html)
Comment on lines +58 to +63
// ✅ Safe — validate both scheme and hostname
const url = new URL(userSuppliedUrl);
const isSafe =
(url.protocol === 'https:' || url.protocol === 'http:') &&
url.hostname === 'yourdomain.com';
<a href={isSafe ? userSuppliedUrl : '#'}>Click here</a>

The same risk applies to `.outerHTML` and `.insertAdjacentHTML()`. Avoid all three when handling dynamic content.

Component-level encapsulation and Shadow DOM do not prevent browser extensions from reading DOM content. Extensions can pierce closed Shadow DOM boundaries using the openOrClosedShadowRoot() API, meaning sensitive data rendered into the DOM remains accessible to any extension with sufficient permissions regardless of the encapsulation strategy used.

Server-side rendered React applications commonly embed initial state in a `<script>` tag for client hydration. Using JSON.stringify for this purpose is unsafe because it does not escape HTML special characters. If the serialized data contains a `</script>` sequence, the browser's HTML parser will terminate the script tag early and may execute attacker-controlled content that follows.

Use a library that explicitly escapes HTML-unsafe sequences such as `</script>` before embedding serialized data in a script tag.[serialize-javascript](https://github.com/yahoo/serialize-javascript) is a commonly used option that was designed for this purpose. Ensure any such library is kept up to date, as serialization libraries have historically been targets for injection vulnerabilities.

In Single Page Applications (SPAs), the React client cannot set httpOnly cookies directly — only the server can. Use a Backend for Frontend (BFF) pattern — a server-side layer such as a Next.js API route or Edge Function — to exchange tokens with the authentication server and set cookies on behalf of the client. The React application never handles the raw token at any point.

Related CWEs: [CWE-922: Insecure Storage of Sensitive Information](https://cwe.mitre.org/data/definitions/922.html), [CWE-284: Improper AccessControl](https://cwe.mitre.org/data/definitions/284.html)

The UI and the server must each enforce their own controls. The server must never trust that the React client has already performed a check.

Related CWEs: [CWE-602: Client-Side Enforcement of Server-Side Security](https://cwe.mitre.org/data/definitions/602.html),[CWE-284: Improper Access Control](https://cwe.mitre.org/data/definitions/602.html)
PRIVATE_API_KEY=sk_live_...
```

If a value is a secret, it belongs in a server-side environment variable accessed only by a server-side process such as a Node.js API route or Edge Function. The VITE_or REACT_APP_ prefix should be reserved exclusively for values that are intentionally public, such as a public-facing API base URL or a publishable key explicitly designed for client-side use.
Comment on lines +534 to +537
CVE-2025-55182 (React2Shell) is a critical unauthenticated Remote Code Execution (RCE) vulnerability affecting React 19.0.0 through 19.2.0. The vulnerability is caused by insecure deserialization in the RSC Flight protocol — the mechanism React uses to transfer server component output to the client. When the server deserializes an RSC payload, it failed to validate whether object properties were own properties or inherited prototype properties. An attacker can craft a malicious HTTP POST request containing __proto__ or constructor keys that pollute the JavaScript prototype chain on the server, which can be chained to execute arbitrary shell commands with full server privileges. The attack requires no authentication and affects any application that uses React Server Components — even those that do not explicitly implement Server Actions.

The vulnerability was assigned a CVSS score of 10.0 (maximum severity), added to CISA's Known Exploited Vulnerabilities (KEV) catalog, and observed being actively exploited in the wild within hours of disclosure.
Upgrade React to 19.3.0 or later and upgrade any RSC-enabled framework such as Next.js to its corresponding patched version. Do not manually parse, construct, or manipulate RSC Flight payloads in application code. Apply framework updates promptly — the RSC serialization layer is a security-critical component and its attack surface will evolve as the ecosystem matures.

## Dependency and Supply Chain Security

React applications depend on large ecosystems of third-party packages. A typical production application has hundreds of transitive dependencies such as packages installed by packages. These include the ones that developer never explicitly chose. Each dependency is a potential attack surface. Supply chain attacks target this ecosystem rather than application code directly: an attacker compromises a package that many applications depend on, causing every downstream application to become vulnerable without any change to its own codebase.

@mackowski mackowski left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for this contribution — it's a strong, well-structured cheat sheet that fills the long-standing gap from #543, and @jmanico has already approved. One issue needs to be fixed before merge, because the remediation as written is incorrect and would mislead readers.

CVE-2025-55182 (React2Shell) — wrong fix versions.

The cheat sheet states:

CVE-2025-55182 (React2Shell) is a critical unauthenticated Remote Code Execution (RCE) vulnerability affecting React 19.0.0 through 19.2.0.

Upgrade React to 19.3.0 or later and upgrade any RSC-enabled framework such as Next.js to its corresponding patched version.

There is no "19.3.0" remediation. Per the official React advisory, the fix was backported as patch releases on each minor line — React 19.0.1, 19.1.2, and 19.2.1. A reader running 19.1.x and told to "upgrade to 19.3.0 or later" would be chasing a version that doesn't exist as the fix for their line, and may stay exposed.

Evidence:

Please reword to point at the patched release for each affected minor line (19.0.1, 19.1.2, 19.2.1) rather than "19.3.0 or later", and cite a primary source. Once that's corrected I'm happy to approve.

@jmanico jmanico left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is too large, and I'd like to to reduced significant. Drop redundant url discussion, references and other data that confuses the issue. We want this to be a cheatsheet that adds value immediately and concisely.

This is good work, see my notes.


Cross-Site Scripting (XSS) occurs when an attacker injects malicious scripts into a web page that are then executed in another user's browser. React escapes dynamic content rendered through JSX by default, converting special characters such as `<`, `>`, and `"` into their HTML-encoded equivalents so the browser treats them as text rather than executable code. This protection applies only to content rendered through JSX. Several React patterns bypass this protection entirely and must be handled with care.

Related CWE(Common Weakness Enumeration): [CWE-79: Improper Neutralization of Input During Web Page Generation (XSS)](https://cwe.mitre.org/data/definitions/79.html)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Lets link to the XSS cheatsheet instead please

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Replacing lines 18 to 22 with "For a comprehensive overview of XSS, see the OWASP Cross-Site Scripting Prevention Cheat Sheet. The following guidance covers React-specific patterns that bypass JSX escaping."

Please confirm. Thank you.

Comment thread cheatsheets_draft/React_Security_Cheat_Sheet.md

### Validate URLs Before Rendering

React does not sanitize the `href`, `src`, or `action` attributes. This creates two distinct risks. First, an attacker can supply a `javascript:` URL which the browser executes as code when the user interacts with the element — a form of DOM-based XSS delivered through an attribute. Second, an attacker can supply a valid `https://` URL pointing to a malicious external site, redirecting users to phishing pages that impersonate your application.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is an complete list. You do not need to list them all but I would suggest there are more.

  • href on , , ,
  • src on , <script>, <iframe>, , ,
  • action on
  • formaction on and (overrides the form's action, routinely missed)
  • data on
  • poster on
  • srcset on and (carries multiple URLs, each needs validation)
  • ping on (fires POST requests to the listed URLs)
  • cite on
    , , ,
  • background (legacy, still parsed in some engines)
  • SVG: href and xlink:href on , , , , plus <script> inside inline SVG
  • CSS url() inside style / background-image (can carry data: or external references)

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@jmanico Good point. I updated to say "common sinks include" and added formaction, srcset, ping, and SVG href to the list rather than implying href/src/action is exhaustive.

Comment thread cheatsheets_draft/React_Security_Cheat_Sheet.md

Where the destination cannot be controlled — such as in user-generated content or comment sections — intercept external navigation with a redirect warning page to inform users they are leaving your application rather than silently following the link.

High-impact URL sinks include `<iframe src>`, `<embed src>`, and `<object data>`. Unlike `<a href>`, these load their target automatically on page render without any user interaction, making them especially dangerous. The `<iframe srcdoc>` attribute renders an HTML string directly as a document inside the frame and must never receive user-supplied content without sanitization. Apply the same scheme and hostname validation to all of these attributes.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this is a bit too much, please collapse this section and just mention above that all URL's rendering attributes are a danger. The "high impact url" section confuses the issue a bit.

Comment thread cheatsheets_draft/React_Security_Cheat_Sheet.md
Comment thread cheatsheets_draft/React_Security_Cheat_Sheet.md

Avoid these patterns entirely. Data-driven logic should use structured data and conditional rendering rather than dynamic code generation.

Related CWEs: [CWE-94: Improper Control of Generation of Code](https://cwe.mitre.org/data/definitions/94.html),[CWE-95: Improper Neutralization of Directives in Dynamically Evaluated Code](https://cwe.mitre.org/data/definitions/95.html)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There are cheatsheets you can drop all the references, just stick to the cheat for developers

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh sure! that means CWEs across all sections should be removed? Please confirm. Thanks.

@anuragnedunuri

Copy link
Copy Markdown
Author

Thanks for this contribution — it's a strong, well-structured cheat sheet that fills the long-standing gap from #543, and @jmanico has already approved. One issue needs to be fixed before merge, because the remediation as written is incorrect and would mislead readers.

CVE-2025-55182 (React2Shell) — wrong fix versions.

The cheat sheet states:

CVE-2025-55182 (React2Shell) is a critical unauthenticated Remote Code Execution (RCE) vulnerability affecting React 19.0.0 through 19.2.0.

Upgrade React to 19.3.0 or later and upgrade any RSC-enabled framework such as Next.js to its corresponding patched version.

There is no "19.3.0" remediation. Per the official React advisory, the fix was backported as patch releases on each minor line — React 19.0.1, 19.1.2, and 19.2.1. A reader running 19.1.x and told to "upgrade to 19.3.0 or later" would be chasing a version that doesn't exist as the fix for their line, and may stay exposed.

Evidence:

Please reword to point at the patched release for each affected minor line (19.0.1, 19.1.2, 19.2.1) rather than "19.3.0 or later", and cite a primary source. Once that's corrected I'm happy to approve.

Thank you for the correction. You are absolutely right. The fix was backported per minor line, not released as a single 19.3.0 version.I will have this corrected. I appreciate the feedback. I will have this updated soon.

@anuragnedunuri

Copy link
Copy Markdown
Author

This is too large, and I'd like to to reduced significant. Drop redundant url discussion, references and other data that confuses the issue. We want this to be a cheatsheet that adds value immediately and concisely.

This is good work, see my notes.

Thank you Jim for the thorough review and detailed feedback. I will work through each point carefully and push the updates shortly. I will fix the em dashes through out the document. I appreciate the time you have taken to ensure the quality of this contribution.

@anuragnedunuri

Copy link
Copy Markdown
Author

@jmanico @mackowski just wanted to check in. I have incorporated the Copilot review suggestions and am actively working through your feedback. Will push the updates shortly.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants