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 all target="_blank" links. However, these target="_blank" attributes are still being removed by DOMPurify.

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');
    }
  }
})