How to Secure React Props in React
Securing React props is essential for preventing Cross-Site Scripting (XSS) attacks, avoiding data leaks, and ensuring application stability. This article provides a straightforward guide on how to protect your React components by implementing strict type checking, validating dynamic data, sanitizing user-generated inputs, and avoiding unsafe prop-spreading practices.
1. Implement Strict Type Checking
The first line of defense in securing props is ensuring that components only receive the data types they expect. This prevents runtime crashes and unexpected behavior caused by malformed data.
Use TypeScript
TypeScript is the industry standard for compile-time type safety in React. By defining explicit interfaces for your props, you catch potential security and logical errors before the code is deployed.
interface UserProfileProps {
username: string;
userId: number;
isAdmin: boolean;
}
const UserProfile = ({ username, userId, isAdmin }: UserProfileProps) => {
return <div>{username} ({userId})</div>;
};
Use PropTypes (Alternative)
If you are not using TypeScript, utilize the prop-types
library for runtime type checking in development mode.
import PropTypes from 'prop-types';
const UserProfile = ({ username, userId, isAdmin }) => {
return <div>{username} ({userId})</div>;
};
UserProfile.propTypes = {
username: PropTypes.string.isRequired,
userId: PropTypes.number.isRequired,
isAdmin: PropTypes.bool
};2. Avoid Blind Prop Spreading
Spreading objects directly onto JSX elements (e.g.,
<div {...props}>) is a common security risk. It can
inadvertently pass sensitive data or invalid attributes directly to the
HTML DOM, which may expose internal state or trigger browser
vulnerabilities.
The Bad Practice:
// If props contains sensitive information, it gets rendered into the DOM
const Button = (props) => {
return <button {...props}>Click me</button>;
};The Secure Practice:
Destructure your props to separate custom component logic from standard HTML attributes.
const Button = ({ isAdmin, label, ...domProps }) => {
// Use isAdmin internally, only spread safe DOM properties
return <button {...domProps}>{label}</button>;
};3. Sanitize User-Generated Props to Prevent XSS
React automatically escapes strings rendered in the JSX body, which protects against basic XSS attacks. However, vulnerabilities still arise when dealing with URLs or raw HTML passed as props.
Securing
dangerouslySetInnerHTML
If a component accepts HTML markup as a prop, you must sanitize it
before rendering. Use a library like dompurify to strip
malicious scripts.
import DOMPurify from 'dompurify';
const RichTextRenderer = ({ rawHtml }) => {
const cleanHtml = DOMPurify.sanitize(rawHtml);
return <div dangerouslySetInnerHTML={{ __html: cleanHtml }} />;
};Securing URL Props
Attackers can inject javascript: pseudo-protocol URLs
into props designed for links (like href or
src). Always validate that URL props use safe protocols
(like http: or https:).
const SafeLink = ({ href, label }) => {
const isSafe = href.startsWith('http://') || href.startsWith('https://') || href.startsWith('/');
return (
<a href={isSafe ? href : '#'} target="_blank" rel="noopener noreferrer">
{label}
</a>
);
};4. Validate Dynamic Props at Runtime
When props are populated by external APIs, compile-time checks like TypeScript cannot guarantee data integrity at runtime. Use a schema validation library like Zod to parse and validate incoming data before passing it down as props.
import { z } from 'zod';
// Define the schema
const UserSchema = z.object({
id: z.string().uuid(),
email: z.string().email(),
age: z.number().min(18),
});
// Validate API data before passing it to components
const handleApiResponse = (data: unknown) => {
const result = UserSchema.safeParse(data);
if (!result.success) {
console.error("Invalid prop data received", result.error);
return null;
}
return <UserProfileUser props={result.data} />;
};