Sanitizer:给你的DOM消消毒
source link: https://segmentfault.com/a/1190000040809349
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
大家好,我卡颂。
业务中经常遇到需要处理有风险的DOM的场景,比如:
- 各种工具的文本粘贴功能
- 需要渲染服务端返回
HTML
的场景
为了阻止潜在的XSS
攻击,有两个选择:
escape
(转义)sanitize
(消毒)
本文会介绍这两者的区别以及为DOM
消毒的API
—— Sanitizer
。
转义与消毒
假设,我们想将这样一段HTML
字符串插入DOM
:
const str = "<img src='' onerror='alert(0)'>";
如果直接将其作为某个元素的innerHTML
,img
的onerror
回调执行JS
代码的能力会带来XSS
风险。
一种常见解决方案是:转义字符串。
什么是escape
浏览器会将一些保留字符解析为HTML
代码,比如:
<
被解析为标签的开头>
被解析为标签的结尾''
被解析为属性值的开头和结尾
为了将这些保留字符显示为文本(不被解析为HTML
代码),可以将其替换为对应的entity
(HTML
实体):
<
的实体为<
>
的实体为>
''
的实体为"
这种将
HTML
字符替换为entity
的方式被称为escape
(转义)
什么是sanitize
对于上面的HTML
字符串:
const str = "<img src='' onerror='alert(0)'>";
除了转义''
来规避XSS
风险,还有一种更直观的思路:直接过滤掉onerror
属性。
这种直接移除
HTML
字符串中有害的代码(比如<script>
)的方式被称为sanitize
(消毒)
需要用到一个API
——Sanitizer。
首先我们通过Sanitizer
构造实例:
const sanitizer = new Sanitizer();
调用实例的sanitizeFor
方法,传入容器元素类型以及要消毒的HTML
字符串:
sanitizer.sanitizeFor("div", str);
会得到一个HTMLDivElement
(即我们传入的容器元素类型),其内部包含一个没有onerror
属性的img
:
默认情况下Sanitizer
会移除所有可能导致JS
执行的代码。
丰富的配置
Sanitizer
不仅开箱即用,还提供丰富的白名单、黑名单配置:
const config = { allowElements: [], blockElements: [], dropElements: [], allowAttributes: {}, dropAttributes: {}, allowCustomElements: true, allowComments: true }; new Sanitizer(config)
比如,allowElements
定义元素白名单,只有名单内的元素会被保留,与之对应的blockElements
是元素黑名单:
const str = `hello <b><i>world</i></b>` new Sanitizer().sanitizeFor("div", str) // <div>hello <b><i>world</i></b></div> new Sanitizer({allowElements: [ "b" ]}).sanitizeFor("div", str) // <div>hello <b>world</b></div> new Sanitizer({blockElements: [ "b" ]}).sanitizeFor("div", str) // <div>hello <i>world</i></div> new Sanitizer({allowElements: []}).sanitizeFor("div", str) // <div>hello world</div>
allowAttributes
是属性白名单,与之对应的dropAttributes
是属性黑名单,对于如下配置:
{ allowAttributes: {"style": ["span"]}, dropAttributes: {"id": ["*"]}} }
代表消毒后的HTML
:
- 只允许
span
元素拥有style
属性 - 移除所有元素(
*
通配符代表所有元素)的id
属性
这么香的API
兼容性怎么样呢:
当前只有在Chrome 93
之后,开启试验标识后可使用:
about://flags/#enable-experimental-web-platform-features
虽然原生Sanitizer
离稳定还遥遥无期,但你可以使用DOMPurify库实现类似功能。
日常你更倾向使用escape
还是sanitize
呢?
欢迎加入人类高质量前端框架研究群,带飞
Recommend
About Joyk
Aggregate valuable and interesting links.
Joyk means Joy of geeK