How to Allow target="blank" in DOMPurify.sanitize()
DOMPurify
is a simple tool to sanitize HTML and reduce cross-site scripting (XSS) vulnerabilities. It’s especially useful to sanitize HTML strings that we want to render in the DOM.
Suppose we’re looking to render the string below (with the anchor tag) as HTML to the DOM.
What is <a href="https://logfetch.com">https://logfetch.com</a>?
We can set the innerHTML
of the div
using dangerouslySetInnerHTML
(in React) and sanitize it using DOMPurify
.
<div dangerouslySetInnerHTML={
{ __html: DOMPurify.sanitize(text) }
} />
However, if we add target="_blank"
to the anchor tag, it’ll be removed by DOMPurify
.
Why is target="_blank"
removed by DOMPurify
?
Some properties like target="_blank"
are removed by DOMPurify
due to performance and security issues.
The target="_blank"
attribute gives the linked page access to our page’s window
object, allowing that page to perform redirects on our page.
Using this attribute can also slow down our page if the linked page is running a lot of JavaScript.
To circumvent this, we can add rel="noopener"
or rel="noreferrer"
to any link that contains target="_blank"
.
As of 2021, current versions of major browsers follow
rel="noopener"
for alltarget="_blank"
links. However, thesetarget="_blank"
attributes are still being removed byDOMPurify
.
When sanitized, the following string:
What is <a href="https://logfetch.com" target="_blank">https://logfetch.com</a>?
becomes this string:
What is <a href="https://logfetch.com">https://logfetch.com</a>?
Allowing target="_blank"
in DOMPurify
We can add hooks to DOMPurify
to extend or alter its functionality.
In this instance, we want to change the target
attribute name prior to sanitization (to avoid sanitization), and then change it back after sanitization.
const TEMP_ATTR = 'temp-target'
DOMPurify.addHook('beforeSanitizeAttributes', function (n) {
if (n.tagName === 'A') {
if (n.hasAttribute('target')) {
n.setAttribute(TEMP_ATTR, n.getAttribute('target'));
}
}
})
DOMPurify.addHook('afterSanitizeAttributes', function (n) {
if (n.tagName === 'A' && n.hasAttribute(TEMP_ATTR)) {
n.setAttribute('target', n.getAttribute(TEMP_ATTR));
n.removeAttribute(TEMP_ATTR);
if (n.getAttribute('target') === '_blank') {
n.setAttribute('rel', 'noopener');
}
}
})