Front-end security refers to the practices and measures taken to secure the user interface and client-side components of a web application.
Update dependencies regularly 📦
- Regularly update React JS and its dependencies to the latest versions to benefit from security patches and bug fixes.
- Use tools like npm audit or yarn audit to identify and address vulnerabilities in your project's dependencies.
- Use Snyk - to monitor and secure the dependencies.
- Use Dependabot - Automated dependency update
Cross-Site Scripting (XSS) Prevention 🛡️
- Use JSX for rendering dynamic content - automatically escapes values to prevent XSS attacks. Avoid using dangerouslySetInnerHTML unless absolutely necessary, and properly sanitize user-generated content before rendering.
In React.js, dangerouslySetInnerHTML is a prop that allows you to inject raw HTML into a component's output. This prop should be used with caution, as improper usage can expose your application to potential security vulnerabilities like cross-site scripting (XSS) attacks.
Avoid: ❌
function MyComponent({ htmlContent }) {
return <div dangerouslySetInnerHTML={{ __html: htmlContent }} />;
}
DO: ✅
import DOMPurify from 'dompurify';
// ...
function renderUserContent(content) {
const sanitizedContent = DOMPurify.sanitize(content);
return { __html: sanitizedContent };
}
function ContentDisplay({ content }) {
return <div dangerouslySetInnerHTML={renderUserContent(content)} />;
}
Content Security Policy (CSP) 🔒
Implement a CSP to control which scripts and resources can be loaded and executed on your web page. This prevents unauthorized scripts from running.
When you're building a React JS application, it's important to make sure that the CSP headers are set correctly on the server. Additionally, you should avoid using unsafe practices like inline event handlers or inline styles, as they might conflict with your CSP policy.
For example:
Instead of using Click me, you should use React's event handling like:
Avoid: ❌
<button onclick="doSomething()">Click me</button>
DO: ✅
<button onClick={doSomething}>Click me</button>
Similarly, avoid using inline styles and use CSS classes instead.
Avoid: ❌
<div style="color: red;">Hello</div>
DO: ✅
<div style="text-color">Hello</div>
React Helmet 🎩
- Use the react-helmet package is used in React JS applications to manage and control the content of the HTML section. This is useful for setting metadata, titles, styles, and other elements in the head section.
DO: ✅
import React from 'react';
import { Helmet } from 'react-helmet';
function MyPage() {
return (
<div>
<Helmet>
<title>My Page Title</title>
<meta name="description" content="This is my page's description." />
<link rel="stylesheet" href="path/to/styles.css" />
</Helmet>
{/* Other JSX content for your page */}
</div>
);
}
export default MyPage;
- Set the Content-Security-Policy header using react-helmed to enforce security policies on the server side.
Express example:
DO: ✅
const express = require('express');
const helmet = require('helmet');
const { Helmet } = require('react-helmet');
const React = require('react');
const ReactDOMServer = require('react-dom/server');
const app = express();
app.use(helmet());
app.get('/', (req, res) => {
const helmetData = Helmet.renderStatic(); // Collect helmet data from your React components
const contentSecurityPolicy = "default-src 'self'; script-src 'self' 'unsafe-inline'; style-src 'self' 'unsafe-inline';"; // Your CSP rules
res.set({
'Content-Security-Policy': contentSecurityPolicy,
...helmetData.meta.toComponent(),
...helmetData.title.toComponent(),
// Add other headers if needed
});
const reactApp = ReactDOMServer.renderToString(React.createElement(YourAppComponent));
res.send(`
<!DOCTYPE html>
<html>
<head>
${helmetData.title.toString()}
${helmetData.meta.toString()}
</head>
<body>
<div id="root">${reactApp}</div>
</body>
</html>
`);
});
app.listen(3000, () => {
console.log('Server is running on port 3000');
});
Next.js example
DO: ✅
import { NextSecureHeadersMiddleware } from 'next-secure-headers';
const securityHeaders = [
{
key: 'Content-Security-Policy',
value: "default-src 'self'; script-src 'self' 'unsafe-inline'; style-src 'self' 'unsafe-inline';",
},
// Add other headers if needed
];
export default NextSecureHeadersMiddleware({
headers: securityHeaders,
});
Input Validation and Sanitization 🧹
- Validate and sanitize inputs on the client side to prevent injection attacks. - Use HTML attributes like required, min, max, type, etc. to enforce basic input validation.
DO: ✅
function MyForm() {
const handleSubmit = (event) => {
event.preventDefault();
// Validate inputs here
};
return (
<form onSubmit={handleSubmit}>
<input type="text" required pattern="[A-Za-z]+" />
<button type="submit">Submit</button>
</form>
);
}
Avoid: ❌
<form action="/register" method="post">
<label for="username">Username:</label>
<input type="text" id="username" name="username" pattern="[A-Za-z0-9]+" required>
<label for="password">Password:</label>
<input type="password" id="password" name="password" pattern=".{8,}" required>
<button type="submit">Register</button>
</form>
- Input Sanitization
You can use third-party libraries to simplify validation and improve user experience. Libraries like react-hook-form, Formik, and yup provide tools for managing forms, validation, and error handling:
DO: ✅
import { useForm } from 'react-hook-form';
import * as yup from 'yup';
const schema = yup.object().shape({
username: yup.string().required(),
email: yup.string().email().required(),
});
function MyForm() {
const { register, handleSubmit, errors } = useForm({
validationSchema: schema,
});
const onSubmit = (data) => {
// Handle form submission
};
return (
<form onSubmit={handleSubmit(onSubmit)}>
<input name="username" ref={register} />
<input name="email" ref={register} />
<button type="submit">Submit</button>
{errors.username && <p>Username is required</p>}
{errors.email && <p>Invalid email</p>}
</form>
);
}
HTTPS usage 🔒
Serve your React app over HTTPS protocol to ensure encrypted communication between client and server.
Secure Authentication and Authorization 🔐
- Implement proper authentication and authorization mechanisms, such as OAuth, JWT, or cookie, and ensure secure storage of auth tokens.
- Authorize users based on roles and permissions to restrict access to sensitive information.
- Leverage established authentication, like Next.js Auth, passport to handle authentication flows securely. These libraries have been designed to handle common security concerns.
Secure State Management 🔒
Use libraries like Redux and React Content API to manage the application state.
Third-Party Libraries and Components 📚
- Only use trusted and well-maintained third-party libraries and components.
- Regularly view and update dependencies to avoid known vulnerabilities.
Protect Sensitive Data with Encryption 🔑
Ensure sensitive data such as user passwords, API keys, and tokens are adequately encrypted. Utilize encryption libraries like bcrypt or crypto js to secure sensitive information and mitigate the impact of data breaches.
DO: ✅
// Client Side - BcryptJS
import React from 'react';
import bcrypt from 'bcryptjs';
function RegistrationForm() {
const handleSubmit = async (event) => {
event.preventDefault();
const plainPassword = event.target.password.value;
const hashedPassword = await bcrypt.hash(plainPassword, 10);
// Send hashedPassword to the server for storage
// ...
};
return (
<form onSubmit={handleSubmit}>
<input type="password" name="password" placeholder="Password" />
<button type="submit">Register</button>
</form>
);
}
export default RegistrationForm;
// Client Side - CryptoJS
import React from 'react';
import CryptoJS from 'crypto-js';
function EncryptDecryptExample() {
const plaintext = 'sensitive_data';
const secretKey = 'super_secret_key';
// Encrypt data
const ciphertext = CryptoJS.AES.encrypt(plaintext, secretKey).toString();
// Decrypt data
const bytes = CryptoJS.AES.decrypt(ciphertext, secretKey);
const decryptedData = bytes.toString(CryptoJS.enc.Utf8);
return (
<div>
<p>Encrypted: {ciphertext}</p>
<p>Decrypted: {decryptedData}</p>
</div>
);
}
export default EncryptDecryptExample;
Static Code Analysis 📝
Use tools like ESLint and Prettier to enforce coding standards and catch potential security vulnerabilities during the developing phase.
Error Handling 🔧
Handle errors gracefully without revealing sensitive information to the users.
If you like my work and want to support me to work hard, please donate via:
Revolut website payment or use the QR code above.
Thanks a bunch for supporting me! It means a LOT 😍
Top comments (1)
At the Content Security Policy (CSP) section.
DO: ✅
<div style="text-color">Hello</div>
Should be
DO: ✅
<div class="text-color">Hello</div>
Right?