Cross Site Scripting (XSS) Attacks in React

Lance Watanabe
Don’t Leave Me Out in the Code
4 min readAug 21, 2020

What are Cross Site Scripting (XSS) Attacks? It means an attacker manipulates your web application to execute malicious code (i.e. JavaScript scripts).

What are the ramifications? The attacker can gain sensitive information, spread worms, or control other users’ browsers (steal cookies to log in as someone else).

How does this happen? I will demonstrate three XSS vulnerabilities that can occur in React: 1) eval, 2) href, and 3) dangerouslySetHTML.

eval(): The eval function evaluates strings as JavaScript. Therefore, if the attacker injects JavaScript into eval(), your app would run the attacker’s script.

In the component below, we have two input forms that request the user’s first and last name as strings. Then, we call eval() to concatenate the first name + last name to form the full name (We do not include a space in the concatenation for the purposes of this example). However, if the user inputs alert(‘Hacked!’) into the first input and presses submit, the alert() function will execute. Note, since the user didn’t provide a last name, the “full name” will be in the first input.

import React, { useState } from 'react';const Eval = () => {
const [data, setData] = useState({
firstName: '',
lastName: '',
});
const handleType = (e) => {
setData({
...data,
[e.target.name]: e.target.value,
});
};
const handleSubmit = () => {
eval(data.firstName + data.lastName);
};
return (
<div>
<input
type='text'
name='firstName'
value={data.firstName}
onChange={(e) => handleType(e)}
/>
<input
type='text'
name='lastName'
value={data.lastName}
onChange={(e) => handleType(e)}
/>
<button onClick={() => handleSubmit()}>Submit</button>{' '}
</div>
);
};
export default Eval;

href: In the component below, we ask the user to provide a link. We set the href dynamically in the <a> tag to the user’s provided link. See bold below. Instead of providing a link, the attacker inputs javascript: alert(‘Hacked!’) so when someone clicks on the link, the alert() will be executed.

import React, { useState } from 'react';const Href = () => {
const [data, setData] = useState({
text: '',
});
const handleType = (e) => {
setData({
...data,
[e.target.name]: e.target.value,
});
};
return (
<div>
<input
type='text'
name='text'
value={data.text}
onChange={(e) => handleType(e)}
/>
<a href={data.text}>click Here</a>
</div>
);
};
export default Href;

dangerouslySetHTML: The name says it all. Each HTML element in JSX has a dangerouslySetHTML property. You can pass HTML into this property as a string and the HTML will be rendered in the DOM.

In the component below, we pass an <img> element into the dangerouslySetHTML property. The only problem is the <img> element includes onerror = ‘alert(\”Hacked!\”)’ which is malicious JavaScript that will be executed if the image is not loaded.

As you can imagine, if you allow the user to dynamically input HTML into the dangerouslySetHTML property, your app would be susceptible to XSS.

It should be noted that React attempts to prevent XSS because it omits<script> tags that are passed into the dangerouslySetHTML property.

import React from 'react';const DangerouslySetInnerHTML = () => {
const createMarkup = () => {
return {
__html: "<img onerror='alert(\"Hacked!\");' src='invalid-image' />",
};
};
return (
<div>
<div dangerouslySetInnerHTML={createMarkup()} />
</div>
);
};
export default DangerouslySetInnerHTML;

How can we prevent these attacks? You can escape (replace) reserved characters (such as< and >) with their respective character entities (&lt; and &gt;). Therefore, when the code is rendered, no JavaScript can be executed. Instead, the character entities will be converted to their respective reserve characters.

Also, you can “sanitize” user inputs using a library called dompurify.

https://github.com/cure53/DOMPurify

XSS Protection in React: When we create new elements using the React API, React will automatically review data to auto-escape scripting code. Below is a snippet of the React’s createElement() method.

return React.createElement("p", {}, review);

--

--