If you’re creating any dynamic web page these days, making sure you have relevant and up-to-date information is vital.
Whether it’s showing stock quotes, the current time, the user’s name, or any other piece of info that changes with time or user, you’ll want to make sure you’re regularly updating this dynamic content to produce the best experience for your users.
Luckily for you, we can associate these dynamic pieces of information with HTML elements and easily display their current state using the innerHTML property.
This blog post will walk you through the pros and cons of using the
innerHTML property and how you can use it safely without opening the doors to potential Cross-Site scripting (XSS) attacks.
innerHTML is an HTML element property that has two uses for web developers:
1) You can use it to get the internal HTML content of any HTML element as an HTML string.
2) You can also use it to set or change elements’
Take the following HTML example:
<div id=”someDivElement”> <span>Hello World</span> </div>Code language: HTML, XML (xml)
To access the innerHTML you’ll first need to access the element itself with
const someDivElement = document.getElementById(“someDivElement”);
Then we can access the
This will give us
"<span>Hello World</span>" as a string. And if you want to modify the
innerHTML property of the element, it can be achieved in the following way:
someDivElement.innerHTML = “<span>Something just like this…</span>”;
Here’s an example that can be used by to-do apps where list items are appended to an existing list:
This will add all the to-do items as
<li>tags in our ordered list element. These are the ways we can get and set the
However – as with any piece of code – there can be exceptions. A common one you may see with
innerHTML is the
SyntaxError, which is thrown when the provided HTML string is ill-formed. Here’s an example:
someDivElement.innerHTML = “<span>William Bradley "Brad" Pitt</span>”Code language: HTML, XML (xml)
The moment the browser parses this script,
"Brad" is perceived as an unknown identifier because of the double quotes, and the
SyntaxError exception is thrown.
When not to use innerHTML
innerHTML is fine if you’re using it to get the value of an element’s
innerHTML content. Things change, however, if it’s used to set values, as it accepts all HTML tags, including the
script tag. This means you could potentially open up a portal to your cookies and users’ personal information via a Cross Site Scripting (XSS) attack.
An XSS attack is a type of web security vulnerability that allows an attacker to inject malicious code into a webpage. Then that code is executed by the web browser when an unsuspecting user visits the page.
Take the example below that allows a bad script to send a user’s cookies to its server, which then can be used to impersonate the actual user and perform malicious actions:
document.getElementById(“someLogoutButton”).innerHTML = “<script>callHome(document.cookie);</script>”;Code language: HTML, XML (xml)
Fortunately, the World Wide Web Consortium (W3C) has since released a standard on dynamic markup insertion in HTMLthat states that
script elements are not executed when inserted using
innerHTML. However, hackers have found other ways to carry out XSS attacks.
Let’s take the following example of the
img tag. It has an
<img src=123 onerror=alert(“Haha!”)>
Once browsers parse this tag, the value of
src is invalid as browsers expect a URL. Thus, an error will be thrown, and as the image tag has an
onerror listener, the script inside will be executed.
How to use innerHTML without creating XSS vulnerabilities
To prevent XSS issues, you always want to sanitize your user input, especially when it’s going to be rendered as-is.
Say you’re building a WYSIWYG editor in which you’re letting users save their HTML content in your database via innerHTML. Since you can’t trust your users’ input, you’ll need to implement some sanitization to prevent them from adding malicious code.
There are a lot of open-source libraries available that provide this sanitization service. One of my favorites is sanitize-HTML, which provides HTML cleaning for browser and node environments.
npm install sanitize-HTML
The library is fairly easy to use. Here are a few examples:
NOTE: You can try the library out at RunKit.
By default, sanitize-HTML has a predefined set of rules on what tags and attributes to allow and remove/escape; however, it can be tailored (refer to the documentation) further per the developers’ requirements.
⚠️ Always sanitize HTML string inputs on both the front and back end to reduce the attack vector substantially.
innerHTML vs. createElement
createElement is an alternative method of innerHTML, so here we’ll look at the two.
createElement is faster, as browsers are not required to parse the HTML string and then build a node tree out of it; it also doesn’t have to attach event listeners as
innerHTML does. Using
innerHTML will cause browsers to reparse and recreate all DOM nodes inside the element whose
innerHTML is modified.
However, if you’re writing a dynamic solution like a Markdown to HTML converter with a real-time preview, then
innerHTML is the way to go as it is a “one-size-fits-all” approach. This is precisely what’s required for this particular conversion and real-time preview. Building the same logic with
createElement would be a pain and render highly coupled logic with unextendable code.
We can see this if we take the same to-do list example discussed above but use
createElement instead of
This will work for this particular problem, but it’s not a scalable approach.
You’d be adding a lot of work for yourself and your team if, for example, you wanted to build a Rich Text Editor where you have to write separate logic for each of the new functionalities you might add.
Using innerHTML with a simple HTML sanitizer is a much more efficient and practical approach.
In HTML, assigning a string to the
innerHTML property is okay in some situations. However, if you can’t be sure of what the string contains—for example, if a user provides it and it is possibly malicious—it’s best to use
createElement or sanitize the input before storing it in a database.
The open-source “sanitization” libraries strip off specific tags and attributes to make the desired HTML string XSS proof. Sanitization should be done on both the frontend and backend as a best practice to help reduce the risk of XSS attacks. When implementing sanitization on the frontend, it should be done at the time of render when the user provides input. And, on the backend, it should be done before storage in the database.
This post was written by Keshav Malik. Keshav is a full-time developer who loves to build and break stuff. He is constantly looking for new and exciting technologies and enjoys working with diverse technologies in his spare time. He loves music and plays badminton whenever the opportunity presents itself.