Sanitizer:給你的DOM消消毒

卡頌發表於2021-10-19

大家好,我卡頌。

業務中經常遇到需要處理有風險的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實體):

  • <的實體為&lt;
  • >的實體為&gt;
  • ''的實體為&quot;
這種將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呢?

歡迎加入人類高質量前端框架研究群,帶飛

相關文章