In this blog, we are going to discuss how to make use of innerHTML in React Applications in a secure way.
Consider a scenario where the user input is HTML tags and the application must display according to the input given by the User. In the following code examples, I have considered a bio input for the user profile.
Let's try using the regular way of taking input a string and displaying using useState Hook
import { useState } from "react";
export default function WithoutInnerHTML() {
const [bio, setBio] = useState("");
const [userBio, setUserBio] = useState("");
const handleChange = (e) => {
setBio(e.target.value);
};
const handleSubmit = () => {
setUserBio(bio);
};
return (
<div>
<label>Bio : </label>
<textarea
placeholder="Enter some html tags"
type="text"
value={bio}
onChange={handleChange}
/>
<button onClick={handleSubmit}>Submit</button>
<div>{userBio}</div>
</div>
);
}
It is being displayed as a regular string instead of parsing the HTML tags and displaying them
Now lets try directly accessing the innerHTML property in React
<div innerHTML={formattedMessage}></div>
If we try using the above syntax it gives the following error
Is there any way of using innerHTML mentioned in the documentation
YES, It is mentioned that with the help of dangerouslySetInnerHTML property on a HTML tag we can set the innerHTML content Documentation
Code Example :
export default function InnerHTML() {
const [bio, setBio] = useState("");
const [userBio, setUserBio] = useState("");
const handleChange = (e) => {
setBio(e.target.value);
};
const handleSubmit = () => {
setUserBio(bio);
setBio("");
};
return (
<div>
<label>Bio : </label>
<textarea
type="text"
value={bio}
placeholder="Enter some html tags"
onChange={handleChange}
/>
<button onClick={handleSubmit}>Submit</button>
<div dangerouslySetInnerHTML={{ __html: userBio }}></div>
</div>
);
}
But Is this the secure way to do it?
Try putting the following code and submit
<img src=?? onerror="alert(`You are hacked ๐`)" />
Once you submit the above code it results in an alert.
So we can say that the user is able to run a javaScript code on our webpage which can lead to a Cross-Site Scripting Attack
How to prevent such things ?
We can prevent the execution of such code input with the help of an npm package called DOMpurify
Final code using DOMpurify sanitize function :
import DOMpurify from "dompurify";
export default function WithDOMpurify() {
const [bio, setBio] = useState("");
const [userBio, setUserBio] = useState("");
const handleChange = (e) => {
setBio(e.target.value);
};
const handleSubmit = () => {
setUserBio(bio);
setBio("");
};
return (
<div>
<label>Bio : </label>
<textarea
type="text"
value={bio}
placeholder="Enter some html tags"
onChange={handleChange}
/>
<button onClick={handleSubmit}>Submit</button>
<div
dangerouslySetInnerHTML={{ __html: DOMpurify.sanitize(userBio) }}
></div>
</div>
);
}
Now if we try to execute the same code which resulted in an alert, It doesn't show any alert. It's because of the DOMpurify package. If you can inspect and see the output the DOMpurify.sanitize function removes the onerror property of the img tag.
Summary :
If we want to display some user input HTML on the react use dangerouslySetInnerHTML along with the DOMpurify.sanitize function. CodeSandBox with all the above codes.