HTML Sanitizer API

可用性有限

此特性不是基線特性,因為它在一些最廣泛使用的瀏覽器中不起作用。

實驗性: 這是一項實驗性技術
在生產中使用此技術之前,請仔細檢查瀏覽器相容性表格

HTML Sanitizer API 允許開發人員獲取 HTML 字串,並在將其插入到 DOM 或 Shadow DOM 時,過濾掉不需要的元素、屬性和其他 HTML 實體。

概念與用法

Web 應用程式通常需要在客戶端處理不受信任的 HTML,例如,作為客戶端模板解決方案的一部分,渲染使用者生成的內容,或者包含來自其他站點的資料到框架中。

注入不受信任的 HTML 會使站點容易受到各種型別的攻擊。特別是,跨站指令碼 (XSS) 攻擊透過將不受信任的 HTML 注入到 DOM 中來工作,然後在當前源的上下文中執行 JavaScript,從而允許惡意程式碼像從站點源提供的那樣執行。可以透過在將不安全的 HTML 元素和屬性注入到 DOM 之前將其刪除來緩解這些攻擊。

HTML Sanitizer API 提供了許多方法,用於在將 HTML 輸入注入到 DOM 之前,從 HTML 輸入中刪除不需要的 HTML 實體。這些方法分為 XSS 安全版本(強制刪除所有不安全的元素和屬性)和可能不安全的版本(開發人員可以完全控制允許的 HTML 實體)。

淨化方法

HTML Sanitizer API 提供 XSS 安全和 XSS 不安全的方法,用於將 HTML 字串注入到 ElementShadowRoot 中,以及將 HTML 解析為 Document 中。

所有方法都接受要注入的 HTML 和可選的淨化器配置作為引數。該配置定義了在注入之前將從輸入中過濾掉的 HTML 實體。Element 方法是上下文感知的,並且還會刪除 HTML 規範不允許在目標元素中出現的任何元素。

安全方法始終刪除 XSS 不安全的元素和屬性。如果未將淨化器作為引數傳遞,它們將使用預設淨化器配置,該配置允許除已知不安全的元素和屬性(例如 <script> 元素和 onclick 事件處理程式)之外的所有元素和屬性。如果使用自定義淨化器,它將隱式更新以刪除任何非 XSS 安全的元素和屬性(請注意,傳遞的淨化器不會被修改,並且如果與不安全方法一起使用,仍可能允許不安全的實體)。

在注入不受信任的 HTML 內容時,應使用安全方法而不是 Element.innerHTMLElement.outerHTMLShadowRoot.innerHTML。例如,在大多數情況下,您可以使用帶有預設淨化器的 Element.setHTML() 作為 Element.innerHTML 的直接替換。同樣的方法也可以用於注入不需要包含任何 XSS 不安全元素的受信任 HTML 字串。

XSS 不安全方法將使用作為引數傳遞的任何淨化器配置。如果沒有傳遞淨化器,則將注入上下文允許的所有 HTML 元素和屬性。這類似於使用 Element.innerHTML,但該方法將解析 shadow roots,刪除不適合上下文的元素,並允許在使用屬性時不允許的一些其他輸入。

不安全方法只應用於包含某些 XSS 不安全元素或屬性的不可信 HTML。這仍然不安全,但允許您透過將不安全實體限制在最小集合來降低風險。例如,如果您想注入不安全的 HTML,但由於某種原因您需要輸入包含 onblur 處理程式,您可以透過修改預設淨化器並使用不安全方法來更安全地實現,如下所示:

js
const sanitizer = Sanitizer(); // Default sanitizer
sanitizer.allowAttribute("onblur"); // Allow onblur

someElement.setHTMLUnsafe(untrustedString, { sanitizer });

淨化器配置

淨化器配置定義了當使用淨化器時,哪些 HTML 實體將被允許、替換或移除,包括元素、屬性、資料屬性和註釋。

有兩個非常密切相關的淨化器配置介面,其中任何一個都可以傳遞給所有淨化方法。

  • SanitizerConfig 是一個字典物件,它定義了在使用配置時允許或不允許的元素或屬性陣列,以及指示是否允許或省略註釋和資料屬性的屬性等。
  • Sanitizer 本質上是 SanitizerConfig 的一個包裝器,它提供了方法來符合人體工程學地、一致地從配置中的各種列表中新增和刪除實體。例如,您可以使用一個方法新增一個允許的屬性,它也會從不允許的陣列中刪除該屬性(如果存在)。該介面還提供了返回底層 SanitizerConfig 副本以及更新淨化器以使其 XSS 安全的方法。它可能會提供用於構建它的淨化器配置的標準化,使其更容易理解和重用。

雖然您可以在任何淨化方法中使用這兩種介面,但 Sanitizer 可能比 SanitizerConfig 更有效地共享和重用。

允許和移除配置

您可以透過兩種方式構建配置:允許配置和移除配置。

在“允許配置”中,您指定要*允許*的元素和屬性(或替換為子元素):輸入中的所有其他元素/屬性都將被刪除。例如,以下配置僅允許 <p><div> 元素,以及任何元素上的 citeonclick 屬性。它還會將 <b> 元素替換為其子節點,有效地剝離其巢狀內容的樣式。

js
const sanitizer = Sanitizer({
  elements: ["p", "div"],
  replaceWithChildrenElements: ["b"],
  attributes: ["cite", "onclick"],
});

當使用允許配置時,很容易理解當解析 HTML 時,哪些元素將在 DOM 中被允許。當您確切知道要在特定上下文中注入哪些 HTML 實體時,它們非常有用。

在“移除配置”中,您指定要移除的 HTML 元素和屬性:淨化器允許任何其他元素和屬性(但如果您使用安全淨化器方法,或者如果元素在上下文中不允許,則可能會被阻止)。例如,以下淨化器將移除與前一個程式碼中允許的相同元素:

js
const sanitizer = Sanitizer({
  removeElements: ["p", "div", "b"],
  removeAttributes: ["cite", "onclick"],
});

當您想使用預設淨化器設定,但可能限制一些額外的實體時,移除配置很有用。

不鼓勵同時使用允許和移除配置,因為它會使配置更難理解且解析效率更低。請注意,同時包含允許和移除實體的配置總是可以簡化為允許配置,其中原始移除列表中的任何實體都已刪除。

如果您將包含允許和移除配置的 SanitizerConfig 傳遞給淨化器方法,它們將丟擲 TypeError。在使用 Sanitizer 時,可以建立同時包含允許和移除列表的配置。如果允許列表包含任何專案,則首先解析允許列表,然後解析移除列表。如果允許列表中有任何專案,則移除列表影響很小。

淨化和可信型別

可信型別 API 提供了機制,確保輸入在傳遞給可能執行該輸入的 API 之前,先透過使用者指定的轉換函式。此轉換函式最常用於淨化輸入,但它並非必須如此:此 API 的主要目的是讓開發人員輕鬆審計淨化程式碼,而不是定義如何或是否進行淨化。

安全的 HTML 淨化方法不需要與可信型別一起使用。因為它們總是在輸入 HTML 注入之前過濾所有 XSS 不安全的實體,所以不需要淨化輸入字串或審計這些方法。

然而,不安全的 HTML 淨化方法可能會根據淨化器注入不受信任的 HTML,因此將與可信型別一起使用。這些方法可以接受字串或 TrustedType 作為輸入。如果還提供了淨化器,則首先執行轉換函式,然後執行淨化器。

請注意,在這種情況下,轉換函式不必淨化輸入字串(儘管它可以),因為您可以使用淨化器 API 來實現。在這種情況下,可信型別提供的資訊是關於可能不安全的字串在哪裡被注入,從而更容易找到它們並檢查淨化器是否配置得當。

第三方淨化庫

在 Sanitizer API 之前,開發人員通常使用第三方庫(如 DOMPurify)來過濾輸入字串,這些庫可能從可信型別中的轉換函式呼叫。

在注入不受信任的 HTML 字串時,不應再需要使用這些庫。該 API 與瀏覽器整合,並且比外部解析器庫更瞭解解析上下文和允許執行的程式碼。

介面

Sanitizer 實驗性

一個可重用的淨化器配置物件,它定義了在淨化不受信任的 HTML 字串時應允許/移除哪些元素和屬性。它用於將 HTML 字串插入到 DOM 或 Document 中的方法。

SanitizerConfig

一個定義淨化器配置的字典。它可以在與 Sanitizer 相同的地方使用,但使用和重用效率可能較低。

其他介面的擴充套件

XSS 安全方法

Element.setHTML()

將 HTML 字串解析為節點子樹,刪除元素上下文中任何無效的元素。然後刪除淨化器配置不允許的任何元素和屬性,以及任何被認為是 XSS 不安全的元素和屬性(即使配置允許)。然後將該子樹作為元素的子樹插入到 DOM 中。

ShadowRoot.setHTML()

將 HTML 字串解析為節點子樹。然後刪除淨化器配置不允許的任何元素和屬性,以及任何被認為是 XSS 不安全的元素和屬性(即使配置允許)。然後將該子樹作為 ShadowRoot 的子樹插入。

Document.parseHTML()

將 HTML 字串解析為節點子樹。然後刪除淨化器配置不允許的任何元素和屬性,以及任何被認為是 XSS 不安全的元素和屬性(即使配置允許)。然後將該子樹設定為 Document 的根。

XSS 不安全方法

Element.setHTMLUnsafe()

將 HTML 字串解析為節點子樹,刪除元素上下文中任何無效的元素。然後刪除淨化器不允許的任何元素和屬性:如果未指定淨化器,則允許所有元素。然後將該子樹作為元素的子樹插入到 DOM 中。

ShadowRoot.setHTMLUnsafe()

將 HTML 字串解析為節點子樹。然後刪除淨化器不允許的任何元素和屬性:如果未指定淨化器,則允許所有元素。然後將該子樹作為 ShadowRoot 的子樹插入。

Document.parseHTMLUnsafe()

將 HTML 字串解析為節點子樹。然後刪除淨化器不允許的任何元素和屬性:如果未指定淨化器,則允許所有元素。然後將該子樹設定為 Document 的根。

示例

以下示例展示瞭如何使用 預設 淨化器來使用淨化器 API(截至撰寫本文時,配置操作尚未支援)。

使用帶有預設淨化器的 Element.setHTML()

在大多數情況下,使用預設淨化器呼叫 Element.setHTML() 可以作為 Element.innerHTML 的直接替換。以下程式碼演示了該方法如何用於在將 HTML 輸入注入到 id 為 target 的元素之前對其進行淨化。

js
const untrustedString = "abc <script>alert(1)<" + "/script> def"; // Untrusted HTML (perhaps from user input)
const someTargetElement = document.getElementById("target");

// someElement.innerHTML = untrustedString;
someElement.setHTML(untrustedString);

console.log(target.innerHTML); // abc def

預設淨化器或 setHTML() 方法不允許使用 <script> 元素,因此 alert() 被刪除。

請注意,使用帶有預設淨化器的 Element.setHTMLUnsafe() 將淨化相同的 HTML 實體。主要區別在於,如果您將此方法與 Trusted Types 一起使用,它可能仍然會被審計。

js
someElement.setHTMLUnsafe(untrustedString);

使用允許淨化器配置

此程式碼展示瞭如何使用 Element.setHTMLUnsafe() 和一個只允許 <p><b><div> 元素的允許淨化器。輸入字串中的所有其他元素都將被移除。

js
const sanitizer = new Sanitizer({ elements: ["p", "b", "div"] });
someElement.setHTMLUnsafe(untrustedString, { sanitizer });

請注意,在這種情況下,您通常應使用 setHTML()。只有在需要允許 XSS 不安全的元素或屬性時,才應使用 Element.setHTMLUnsafe()

規範

規範
HTML Sanitizer API
# sanitizer

瀏覽器相容性