How to Secure JSX in React
React’s JSX syntax provides built-in protections against security threats, but developers must still understand how to handle specific edge cases to keep their applications safe. This article explores how React automatically secures JSX by default, identifies potential vulnerabilities like Cross-Site Scripting (XSS), and outlines essential best practices for securing JSX in your React applications.
How React Protects JSX by Default
React is designed with security in mind. By default, React DOM escapes any values embedded in JSX before rendering them. This means that everything is converted to a string before being rendered to the browser, which effectively prevents Cross-Site Scripting (XSS) attacks.
For example, consider the following code:
const userInput = '<script>badCode()</script>';
const element = <h1>{userInput}</h1>;React automatically converts the angle brackets into HTML entities
(< and >). As a result, the
browser displays the raw script as text on the screen rather than
executing it as executable code.
Key Vulnerabilities in JSX and How to Fix Them
Despite React’s built-in escaping, developers can accidentally bypass these protections. Below are the most common vulnerabilities associated with JSX and how to secure them.
1. The Danger of
dangerouslySetInnerHTML
React provides the dangerouslySetInnerHTML attribute as
a way to insert raw HTML into a component. While sometimes necessary,
using this attribute with unsanitized user input creates an immediate
XSS vulnerability.
// INSECURE
const UserProfile = ({ bio }) => {
return <div dangerouslySetInnerHTML={{ __html: bio }} />;
};How to secure it: Always sanitize HTML content
before passing it to dangerouslySetInnerHTML. Use a
reliable sanitization library like DOMPurify to strip
out harmful scripts while keeping safe HTML tags.
import DOMPurify from 'dompurify';
// SECURE
const UserProfile = ({ bio }) => {
const cleanBio = DOMPurify.sanitize(bio);
return <div dangerouslySetInnerHTML={{ __html: cleanBio }} />;
};2. Unsafe URL Attributes (Attribute Injection)
React does not automatically sanitize URLs used in attributes like
href or src. If a user can input a URL that
begins with javascript:, the browser will execute that
script when a user clicks the link.
// INSECURE
const UserLink = ({ userWebsite }) => {
return <a href={userWebsite}>Visit Profile</a>;
};If userWebsite is set to
javascript:alert('Hacked'), clicking the link runs the
malicious payload.
How to secure it: Validate and whitelist URLs to
ensure they only use safe protocols, such as http: or
https:.
// SECURE
const validateUrl = (url) => {
try {
const parsedUrl = new URL(url);
return ['http:', 'https:'].includes(parsedUrl.protocol) ? url : '#';
} catch (e) {
return '#'; // Fallback for invalid URLs
}
};
const UserLink = ({ userWebsite }) => {
return <a href={validateUrl(userWebsite)}>Visit Profile</a>;
};3. Rendering JSON and Server-Side Rendering (SSR) Hydration
When using Server-Side Rendering, developers often pass initial state
from the server to the client by embedding JSON inside a
<script> tag. If this JSON is not properly escaped,
attackers can inject malicious closing tags
(</script>) to execute arbitrary code.
// INSECURE (SSR)
<script>
window.__INITIAL_STATE__ = ${JSON.stringify(initialState)};
</script>How to secure it: Use specialized serialization
libraries like serialize-javascript or
htmlescape instead of standard
JSON.stringify(). These tools automatically escape HTML
tags inside JSON strings to prevent script injection.
import serialize from 'serialize-javascript';
// SECURE
<script>
window.__INITIAL_STATE__ = ${serialize(initialState, { isJSON: true })};
</script>Quick Checklist for JSX Security
- Avoid
dangerouslySetInnerHTMLunless absolutely necessary. When using it, always sanitize the input with DOMPurify. - Validate protocols for
hrefandsrcattributes to preventjavascript:payload execution. - Keep React updated to ensure you benefit from the latest security patches and built-in protections.
- Use Content Security Policies (CSP) on your web server to act as an extra layer of defense against XSS attacks.