5

Sanitizer:给你的DOM消消毒

 2 years ago
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

本文内容来自Safe DOM manipulation with the Sanitizer API

转义与消毒

假设,我们想将这样一段HTML字符串插入DOM

const str = "<img src='' onerror='alert(0)'>";

如果直接将其作为某个元素的innerHTMLimgonerror回调执行JS代码的能力会带来XSS风险。

一种常见解决方案是:转义字符串。

什么是escape

浏览器会将一些保留字符解析为HTML代码,比如:

  • <被解析为标签的开头
  • >被解析为标签的结尾
  • ''被解析为属性值的开头和结尾

为了将这些保留字符显示为文本(不被解析为HTML代码),可以将其替换为对应的entityHTML实体):

  • <的实体为<
  • >的实体为>
  • ''的实体为"

这种将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呢?

欢迎加入人类高质量前端框架研究群,带飞


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK