How to Secure HashRouter in React
This article provides a practical guide on securing
HashRouter in React applications. We will explore the
unique security challenges associated with hash-based
routing—specifically client-side manipulation and Cross-Site Scripting
(XSS) vulnerabilities—and detail actionable strategies to safeguard your
application, including input sanitization, route guards, and
implementing robust Content Security Policies (CSP).
Understanding HashRouter Security Risks
HashRouter is commonly used in React applications to
support routing in legacy browsers or static hosting environments where
server-side configuration is not possible. It uses the hash portion of
the URL (e.g., window.location.hash, like
#/dashboard) to keep the UI in sync with the URL.
Because the hash portion of the URL is handled entirely on the client side, it introduces specific security vulnerabilities: * Client-Side Manipulation: Users can easily modify the hash in their browser address bar, attempting to bypass navigation flows. * DOM-based Cross-Site Scripting (XSS): If your application reads parameters directly from the hash and dynamically renders them without sanitization, attackers can inject malicious scripts.
To secure your application when using HashRouter,
implement the following security best practices.
1. Implement Route Guards (Authorization Checks)
Never rely on the router itself to restrict access to sensitive pages. Because route changes happen entirely in the client’s browser, you must programmatically enforce authorization checks before rendering protected components.
Create a wrapper component for protected routes to verify the user’s authentication state:
import React from 'react';
import { Navigate } from 'react-router-dom';
const ProtectedRoute = ({ isAuthenticated, children }) => {
if (!isAuthenticated) {
// Redirect unauthorized users to the login page
return <Navigate to="/login" replace />;
}
return children;
};
export default ProtectedRoute;2. Sanitize and Validate URL Parameters
If your routes extract parameters from the hash (e.g.,
#/user/:id), validate and sanitize these parameters before
using them in database queries, API requests, or rendering them to the
DOM.
- Type Cast and Validate: Ensure IDs are numbers or strictly match expected UUID formats.
- Avoid dangerous rendering: Do not use unsanitized
hash parameters inside
dangerouslySetInnerHTML. - Sanitize Inputs: If you must render user-controlled
data, use a sanitization library like
DOMPurify.
import DOMPurify from 'dompurify';
import { useParams } from 'react-router-dom';
const UserProfile = () => {
const { username } = useParams();
// Sanitize the parameter before rendering it to prevent XSS
const safeUsername = DOMPurify.sanitize(username);
return <h1>Profile of {safeUsername}</h1>;
};3. Configure a Robust Content Security Policy (CSP)
A Content Security Policy (CSP) is an essential layer of defense against XSS attacks. By restricting where scripts can be loaded and executed from, you prevent malicious scripts injected via URL hashes from running.
Implement the following CSP headers on your web server:
Content-Security-Policy: default-src 'self'; script-src 'self'; object-src 'none';
This prevents the execution of inline scripts and restricts script execution to trusted source files hosted on your domain.
4. Secure State Management
Do not store sensitive data—such as session tokens, passwords, or
personal identifiable information (PII)—directly in the URL hash or the
router state. Instead, store session tokens in secure,
HttpOnly cookies, or manage application state in-memory
(using React Context or Redux) to prevent access by malicious browser
extensions or third-party scripts.
5. Migrate to BrowserRouter When Possible
The most effective way to secure a React application’s routing is to
migrate from HashRouter to BrowserRouter.
BrowserRouter utilizes the HTML5 History API, sending
routing requests to the server. This allows you to: * Implement
server-side redirects and authentication checks. * Use secure
HttpOnly cookie-based authentication seamlessly. * Avoid
exposing application structure and routing parameters in the URL
hash.