Adding HTML from a string the safe way

April 14th, 2023

Adding HTML from a string isn’t difficult. Something like this works amazingly:

name="John"
html = `<div>Hello <b>${name}</b>,
<p>Welcome on this lovely day</p>
</div>`
document.body.innerHTML=html;

It’s easy and it’s fast, but it is not safe!

Copy and paste above code in your browser console and execute it. It will remove all content, and show the new html.

Let’s assume you get the name from an external resource or user input, instead of John it could be something like `<img src=”z” onerror=”alert(‘Your f*cked’)”`

name=`<img src="z" onerror="alert('Your f*cked')"`
html = `<div>Hello <b>${name}</b>,
<p>Welcome on this lovely day</p>
</div>`
document.body.innerHTML=html;

Copy and paste above code in your browser console and execute it.

It will execute the JavaScript code in the onerror attribute and show an alert. This demo is harmless, but innerHTML is a security hole, that opens the way to severe security risks.

innerHTML is not safe!

But help is on the way, a setHTML function, that can be tweaked by a sanitizer function.

How to use the new and secure way of adding HTML from a string

The syntax for the new function is

setHTML(input, options)

So this will work:

name=`<img src="z" onerror="alert('Your f*cked')"`
html = `<div>Hello <b>${name}</b>,
<p>Welcome on this lovely day</p>
</div>`
document.body.setHTML(html);

Keep in mind innerHTML is a property, and setHTML is an function.

setHTML is working in Chromium and Firefox, although in Firefox it’s behind a config setting. AFAIK it’s still considered experimental. To change it open about:config and toggle the preference to tru

dom.security.setHTML.enabled

Can’t wait until this is shipped as stable.

Somehow the documentation is wrong, it’s not the dom.security.sanitizer.enabled preference , but the `dom.security.setHTML.enabled` that should bet toggled.

The sanitizer preference is for the new Sanitizer() object that can be passed as an option

const attribute_sanitizer =new Sanitizer ( {allowAttributes: {"style": ["div"]}})
document.body.setHTML(unsanitized_string,{sanitizer: attribute_sanitizer}) 

That way you can tweak the elements that you want to be sanitized. Like dropping specified elements, attributes, or comments. Also very useful.

But the default is working fine in Firefox.

Leave a Reply