ShadowRoot: innerHTML 屬性

警告:此屬性將輸入解析為 HTML,並將結果寫入 DOM。像這樣的 API 被稱為注入槽,如果輸入最初來自攻擊者,它們可能是跨站指令碼(XSS)攻擊的潛在途徑。

您可以透過始終分配 TrustedHTML 物件而不是字串並強制執行可信型別來減輕這種風險。有關更多資訊,請參閱安全注意事項

ShadowRoot 介面的 innerHTML 屬性用於獲取或設定 ShadowRoot 內部 DOM 樹的 HTML 標記。

獲取該屬性會返回一個字串,其中包含陰影根(shadow root)的後代節點的 HTML 序列化表示。

設定該屬性接受一個 TrustedHTML 物件或一個字串。它會將此值解析為 HTML,並用解析結果替換元素的所有後代節點。當設定為 null 值時,該 null 值會被轉換為空字串(""),因此 shadowRoot.innerHTML = null 等同於 shadowRoot.innerHTML = ""

異常

SyntaxError DOMException

如果嘗試使用格式不正確的 HTML 字串來設定 innerHTML 的值,將丟擲此異常。

TypeError

如果在可信型別CSP 強制執行且未定義預設策略時將屬性設定為字串,則丟擲此錯誤。

描述

innerHTML 獲取陰影根內巢狀的子 DOM 元素的序列化表示,或者設定應該被解析以替換陰影根內 DOM 樹的 HTML 或 XML。

請注意,當 <> 字元出現在屬性值中時,某些瀏覽器會將其序列化為 &lt;&gt;(請參閱瀏覽器相容性)。這是為了防止潛在的安全漏洞(變異 XSS),其中攻擊者可以精心構造繞過淨化函式的輸入,從而實現跨站指令碼(XSS)攻擊。

安全注意事項

innerHTML 屬性是 跨站指令碼(XSS)攻擊的潛在攻擊向量,在這種攻擊中,使用者提供的潛在不安全字串會在未經驗證的情況下注入 DOM。雖然該屬性可以阻止注入的 <script> 元素執行,但它仍然容易受到攻擊者精心構造的、用於執行惡意 JavaScript 的其他 HTML 方法的攻擊。例如,下面的示例將在 error 事件處理程式中執行程式碼,因為 <img>src 值不是有效的影像 URL。

js
const name = "<img src='x' onerror='alert(1)'>";
shadowRoot.innerHTML = name; // shows the alert

您可以透過始終分配 TrustedHTML 物件而不是字串來緩解這些問題,並透過使用 require-trusted-types-for CSP 指令 強制執行受信任型別。這確保輸入會透過一個轉換函式,該函式有機會在注入之前 清理輸入以移除潛在危險的標記。

示例

讀取元素的 HTML 內容

讀取 innerHTML 會導致使用者代理序列化陰影根的後代節點。

給定以下 HTML

html
<div class="host">
  <template shadowrootmode="open">
    <p>My name is Joe</p>
  </template>
</div>

您可以按照如下所示獲取並記錄陰影根的標記。

js
const shadowHost = document.querySelector("#host");
const shadowRoot = shadowHost.shadowRoot;
const contents = shadowRoot.innerHTML;
console.log(contents); // "\n  <p>My name is Joe</p>\n"

設定 ShadowRoot 的 innerHTML

在此示例中,我們將透過將 HTML 分配給元素的 innerHTML 屬性來替換元素的 DOM。為了降低 XSS 風險,我們將首先從包含 HTML 的字串建立 TrustedHTML 物件,然後將該物件分配給 innerHTML

受信任的型別尚未在所有瀏覽器中得到支援,因此我們首先定義 受信任型別 tinyfill。它充當受信任型別 JavaScript API 的透明替代品。

js
if (typeof trustedTypes === "undefined")
  trustedTypes = { createPolicy: (n, rules) => rules };

接下來,我們建立一個 TrustedTypePolicy,它定義一個 createHTML() 方法,用於將輸入字串轉換為 TrustedHTML 例項。通常,createHTML() 的實現會使用像 DOMPurify 這樣的庫來清理輸入,如下所示:

js
const policy = trustedTypes.createPolicy("my-policy", {
  createHTML: (input) => DOMPurify.sanitize(input),
});

然後我們使用這個 policy 物件從潛在不安全的輸入字串建立 TrustedHTML 物件,並將結果分配給元素。

js
// The potentially malicious string
const untrustedString = "<p>I might be XSS</p><img src='x' onerror='alert(1)'>";

// Create a TrustedHTML instance using the policy
const trustedHTML = policy.createHTML(untrustedString);

// Get the shadow root
const shadowHost = document.querySelector("#host");
const shadowRoot = shadowHost.shadowRoot;

// Inject the TrustedHTML (which contains a trusted string)
shadowRoot.innerHTML = trustedHTML;

警告:雖然您可以直接將字串分配給 innerHTML,但這是一種 安全風險,如果待插入的字串可能包含潛在的惡意內容。

規範

規範
HTML
# dom-shadowroot-innerhtml

瀏覽器相容性