Sanitizer:给你的DOM消消毒
共 2160字,需浏览 5分钟
·
2021-10-23 03:01
源 / 文/ 卡颂
业务中经常遇到需要处理「有风险的DOM」的场景,比如:
各种工具的文本粘贴功能
需要渲染服务端返回
HTML
的场景
为了阻止潜在的XSS
攻击,有两个选择:
escape
(转义)sanitize
(消毒)
本文会介绍这两者的区别以及为DOM
消毒的API
—— Sanitizer
。
![](https://filescdn.proginn.com/df54c4697b436a15c8f887082fae00cb/4dfa49d0c2320d4e66add1879863ec8e.webp)
本文内容来自Safe DOM manipulation with the Sanitizer API[1]
转义与消毒
假设,我们想将这样一段HTML
字符串插入DOM
:
const str = "";
如果直接将其作为某个元素的innerHTML
,img
的onerror
回调执行JS
代码的能力会带来XSS
风险。
一种常见解决方案是:转义字符串。
什么是escape
浏览器会将一些保留字符解析为HTML
代码,比如:
<
被解析为标签的开头>
被解析为标签的结尾''
被解析为属性值的开头和结尾
为了将这些保留字符显示为文本(不被解析为HTML
代码),可以将其替换为对应的entity
(HTML
实体):
<
的实体为<
>
的实体为>
''
的实体为"
这种将
HTML
字符替换为entity
的方式被称为escape
(转义)
什么是sanitize
对于上面的HTML
字符串:
const str = "";
除了转义''
来规避XSS
风险,还有一种更直观的思路:直接过滤掉onerror
属性。
这种直接移除
HTML
字符串中有害的代码(比如)的方式被称为
sanitize
(消毒)
需要用到一个API
——Sanitizer[2]。
首先我们通过Sanitizer
构造实例:
const sanitizer = new Sanitizer();
调用实例的sanitizeFor
方法,传入容器元素类型以及要消毒的HTML
字符串:
sanitizer.sanitizeFor("div", str);
会得到一个HTMLDivElement
(即我们传入的容器元素类型),其内部包含一个没有onerror
属性的img
:
![](https://filescdn.proginn.com/fd6ea7b345f2c4b32802edb73e7e7109/6f8947bf7d41f4ddab3af04912dd516a.webp)
默认情况下Sanitizer
会移除所有可能导致JS
执行的代码。
丰富的配置
Sanitizer
不仅开箱即用,还提供丰富的白名单、黑名单配置:
const config = {
allowElements: [],
blockElements: [],
dropElements: [],
allowAttributes: {},
dropAttributes: {},
allowCustomElements: true,
allowComments: true
};
new Sanitizer(config)
比如,allowElements
定义元素白名单,只有名单内的元素会被保留,与之对应的blockElements
是元素黑名单:
const str = `hello world`
new Sanitizer().sanitizeFor("div", str)
// hello world
new Sanitizer({allowElements: [ "b" ]}).sanitizeFor("div", str)
// hello world
new Sanitizer({blockElements: [ "b" ]}).sanitizeFor("div", str)
// hello world
new Sanitizer({allowElements: []}).sanitizeFor("div", str)
// hello world
allowAttributes
是属性白名单,与之对应的dropAttributes
是属性黑名单,对于如下配置:
{
allowAttributes: {"style": ["span"]},
dropAttributes: {"id": ["*"]}}
}
代表消毒后的HTML
:
只允许
span
元素拥有style
属性移除所有元素(
*
通配符代表所有元素)的id
属性
![](https://filescdn.proginn.com/84b4b4329c6f9ad3377d8beb6d535da9/8508516ff3b06b793dea3b32b335805c.webp)
兼容性
这么香的API
兼容性怎么样呢:
![](https://filescdn.proginn.com/cc6a26183ae202113420091f884b65cc/8e5c53100c9e6d86e5bc302cad3d3080.webp)
![](https://filescdn.proginn.com/fa7310306f92ceacb3333f6d3d96343a/efc56681d4f443510e2480efe53e4084.webp)
当前只有在Chrome 93
之后,开启试验标识后可使用:
about://flags/#enable-experimental-web-platform-features
虽然原生Sanitizer
离稳定还遥遥无期,但你可以使用DOMPurify[3]库实现类似功能。
后记
日常你更倾向使用escape
还是sanitize
呢?
参考资料
Safe DOM manipulation with the Sanitizer API:https://web.dev/sanitizer/
[2]Sanitizer:https://wicg.github.io/sanitizer-api/
[3]DOMPurify:https://github.com/cure53/DOMPurify
END
![](https://filescdn.proginn.com/270e324d5bf04f4357fecc22a2794cc4/9f1a1aaa7d7c3122a7c8a7c18efbf45f.webp)
![](https://filescdn.proginn.com/ebc516369a1226a5858801fa65c2de3c/86a0deca3c8f5749aab6bcce64f84dfa.webp)
顶级程序员:topcoding
做最好的程序员社区:Java后端开发、Python、大数据、AI
一键三连「分享」、「点赞」和「在看」