大家好,我卡頌。
業務中經常遇到需要處理有風險的DOM的場景,比如:
- 各種工具的文字貼上功能
- 需要渲染服務端返回
HTML
的場景
為了阻止潛在的XSS
攻擊,有兩個選擇:
escape
(轉義)sanitize
(消毒)
本文會介紹這兩者的區別以及為DOM
消毒的API
—— Sanitizer
。
本文內容來自Safe DOM manipulation with the Sanitizer API
轉義與消毒
假設,我們想將這樣一段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
呢?
歡迎加入人類高質量前端框架研究群,帶飛