浏览器支持说明:所有浏览器都支持 setHTMLUnsafe 。 setHTML 仍在标准化中,仅在 Firefox 中可用。 getHTML 自版本 125 起在 Chrome 和 Edge 中受支持。
浏览器最近实施了一种新的 setHTMLUnsafe 方法。这里的 Unsafe 意味着,就像 innerHTML 一样,它不执行输入清理。这种命名方式与以前的浏览器 API 并不一致:我们有 innerHTML,但没有 innerHTMLUnsafe;有 eval(),但没有 evalUnsafe(),等等。但与老方法不同的是,它既有安全版本(setHTML),也有不安全版本(setHTMLUnsafe)--这就是命名的由来。
Sanitizer API 规范如下:
“safe”方法不会生成任何执行脚本的标记。也就是说,它们应该不会受到 XSS 的影响。
假设我们有一个带有文本 <input> 的 HTML 表单和一些根据用户提供的值更改 DOM 的 JavaScript 代码:
form.addEventListener('submit', function(event) {
event.preventDefault();
const markup = `<h2>${input.value}</h2>`
div.innerHTML = markup;
});
如果用户在输入中输入 <img src=doesnotexist onerror="alert('Potential XSS Attack')"> ,该 JavaScript 代码将在浏览器中运行。 .setHTMLUnsafe() 也有同样的问题。
在这个简单的示例中,代码仅在用户自己的浏览器中运行,但如果此类用户输入存储在数据库中并用于向其他人显示动态内容,则任意且潜在恶意的 JavaScript 可能会在其他用户的浏览器中运行。
使用 setHTML 时,插入 DOM 的内容只有 <img src="doesnotexist"> 。图像仍被注入页面,但 JavaScript 已被剥离。
Sanitizer API 仍在进行中,但它有助于将 setHTMLUnsafe 的命名放在上下文中。
setHTMLUnsafe
如果我们(希望)能得到 setHTML,而且我们已经有了 innerHTML,那为什么还需要 setHTMLUnsafe 呢?答案就是声明式 shadow DOM。
HTML <template> 元素可以通过两种不同的方式使用:
- 保存未渲染但稍后可以通过 JavaScript 使用的 HTML 片段。
- 立即生成 Shadow DOM。如果
<template>包含shadowrootmode属性,则该元素在 DOM 中将在 Shadow 根内替换为其内容。
innerHTML 可以很好地处理第一个用例,但无法处理第二个用例。
const main = document.querySelector('main');
main.innerHTML = `
<h2>I am in the Light DOM</h2>
<div>
<template shadowrootmode="open">
<style>
h2 { color: blue; }
</style>
<h2>Shadow DOM</h2>
</template>
</div>`
innerHTML 确实将 <template> 注入到页面中,但它仍然是一个 <template> 元素 - 它不会变成shadow DOM,并且它的内容不会被渲染,无论 shadowrootmode 属性如何。
setHTML 将有目的地删除 template 及其内容:
const main = document.querySelector('main');
main.setHTML(`
<h2>I am in the Light DOM</h2>
<div>
<template shadowrootmode="open">
<style>
h2 { color: blue; }
</style>
<h2>Shadow DOM</h2>
</template>
</div>`);
在上面的示例中, main 的内容现在是 h2 和空的 div。该 template 被视为“unsafe node”。
这就是浏览器添加 setHTMLUnsafe 的原因,作为向页面动态添加声明性 shadow DOM 的一种方式。
main.setHTMLUnsafe(`
<h2>I am in the Light DOM</h2>
<div>
<template shadowrootmode="open">
<style>
h2 { color: blue; }
</style>
<h2>Shadow DOM</h2>
</template>
</div>
`);
当使用 setHTMLUnsafe 时, <template> 的内容将在 Shadow DOM 内部渲染。
getHTML
setHTML 和 setHTMLUnsafe 本身并不能完全替代 innerHTML。setHTML 和 setHTMLUnsafe 的补充函数是 getHTML(没有不安全版本)。
const main = document.querySelector('main');
const html = main.getHTML();
默认情况下 getHTML 不会从 shadow DOM 中返回任何标记,但它是可配置的。
const main = document.querySelector('main');
const html = main.getHTML({serializableShadowRoots: true} );
将 serializableShadowRoots 设置为 true 将序列化所有选择序列化的 shadow DOM 树。
template 元素可以使用 shadowrootserializable 属性选择加入:
<template shadowrootmode="open" shadowrootserializable>
类似地,在 JavaScript 中, attachShadow 方法有一个布尔值 serializable 选项。
this.attachShadow({ mode: "open", serializable: true });
还可以通过传递 shadow 根数组,只序列化某些指定的 shadow DOM 树:
const markup= main.getHTML({
shadowRoots: [document.querySelector('.example').shadowRoot]
});
数组中的所有 shadow root 都将被序列化,即使它们没有标记为可序列化。