Trusted Types 是一項安全防護功能,可防止 DOM 型跨網站指令碼攻擊 (DOM XSS),方法是要求傳入「危險」瀏覽器 API (稱為接收器) 的資料,必須先轉換為特定類型,例如 TrustedHTML。
根據預設,許多 DOM API (例如 innerHTML 或 document.write) 都接受原始字串。如果字串含有來自不受信任來源的惡意指令碼,就可能執行。Trusted Types 會改變這項行為:透過 CSP 啟用後,這些接收器只會接受由授權政策建立的 TrustedHTML 物件。
重要導入細節
如要使用 Trusted Types,請按照下列四個主要步驟操作:
- 定義安全性政策,指定如何清理或轉換字串。
- 使用該政策,從原始字串建立 Trusted Type 物件。
- 寫入 DOM 接收器時,請使用 Trusted Type 物件。
- 使用內容安全政策 (CSP) 強制執行 Trusted Types。
1. 建立政策
政策是一組用於清理內容的規則。您可以使用 trustedTypes.createPolicy() 建立,通常會委派給淨化程式庫 (例如 DOMPurify) 或應用程式的 HTML 逸出規則:
import DOMPurify from 'dompurify';
const myPolicy = trustedTypes.createPolicy('sanitize-html', {
createHTML: (input) =>
// Return a sanitized string; createPolicy() wraps it in a TrustedHTML object.
// Example using DOMPurify:
DOMPurify.sanitize(input, { RETURN_TRUSTED_TYPE: false }),
});
// Example using HTML escaping (for environments without third-party sanitizers):
const escapePolicy = trustedTypes.createPolicy('escape-html', {
createHTML: (input) =>
String(input)
.replaceAll('&', '&')
.replaceAll('<', '<')
.replaceAll('>', '>')
.replaceAll('"', '"')
.replaceAll("'", '''),
});
2. 使用政策
請勿直接將字串傳遞至接收器,而是呼叫政策的建立方法。
const untrustedInput = '<p>Hello, <strong>world</strong>!</p><img src="x" onerror="alert(1)">';
// In an enforced environment, this would throw a TypeError:
// element.innerHTML = untrustedInput;
// This returns a TrustedHTML object with unsafe elements and attributes removed:
const cleanHTML = myPolicy.createHTML(untrustedInput);
// Result: "<p>Hello, <strong>world</strong>!</p><img src=\"x\">"
3. 將接收器轉換為使用可信任類型
更新寫入接收器的每個執行個體,先將其轉換為 Trusted Type 物件。如要找出需要更新程式碼的位置,您可以定義暫時的 default 政策,在接收端寫入字串而非 TrustedType 時記錄。
trustedTypes.createPolicy("default", {
createHTML(value) {
console.warn("String passed instead of Trusted Type.");
return DOMPurify.sanitize(value, { RETURN_TRUSTED_TYPE: false });
},
});
支援可信任類型的常見接收器包括:
- HTML:
element.innerHTML、element.outerHTML、document.write()、element.setHTMLUnsafe()、Document.parseHTMLUnsafe() - 指令碼:
<script>文字內容,eval() - ScriptURL:
<script src>、Worker()、SharedWorker()
// Assign the TrustedHTML object.
element.innerHTML = cleanHTML;
請注意,「安全」的 HTML 清理方法 (element.setHTML() 和 Document.parseHTML(),詳情請參閱「清理不受信任的 HTML」) 不需要 Trusted Types 政策,因為這些方法一律會以原生方式清理潛在的 XSS 攻擊。
4. 使用 CSP 強制執行 Trusted Types
Trusted Types 應逐步推出,以免應用程式中斷。雖然完整強制執行可提供最強大的保護,但如果並非所有接收器寫入作業都已遷移,則可能會有網站中斷的風險。
必要條件
- 架構合作:如果應用程式的架構 (或任何寫入 DOM 接收器的第三方程式庫) 未產生
TrustedHTML或TrustedScript值,您就必須破壞該程式碼,才能強制執行可信任類型。在開始推出前,請先檢查架構支援。 - 接收端重構:請務必找出並重構危險接收端,以便使用 Trusted Types 政策。
推出策略
僅限報表:首先,使用
Content-Security-Policy-Report-Only標頭推出。這樣一來,系統就會將違規事項記錄到回報端點,但不會實際封鎖寫入作業。Content-Security-Policy-Report-Only: require-trusted-types-for 'script';強制執行:整合政策後,如果違規報告數量降至零,即可全面強制執行。
Content-Security-Policy: require-trusted-types-for 'script';
您也可以限制可建立的政策 (列出政策名稱,例如 sanitize-html 或 escape-html;如果政策委派給 DOMPurify.sanitize(),請加入 dompurify,這會在內部建立 dompurify 政策;如果您在遷移期間註冊預設政策,請加入 default):
Content-Security-Policy: require-trusted-types-for 'script'; trusted-types sanitize-html escape-html dompurify;
有了這個 CSP,任何不使用 Trusted Types 的接收器寫入作業都會擲回錯誤。此外,如果嘗試建立的 Trusted Type 政策名稱未列在 trusted-types 中,系統會擲回錯誤。
您也可以在文件的 <head> 中將此 CSP 設為 <meta> 標記,但這樣做可能較不安全。
<!-- First child of `<head>`, as earlier tags will not enforce this CSP. -->
<meta
http-equiv="Content-Security-Policy"
content="require-trusted-types-for 'script'; trusted-types sanitize-html escape-html dompurify;"
/>
備用策略
如果瀏覽器尚未支援可信任類型,您可以使用最精簡的「tinyfill」程式碼,確保程式碼不會中斷,同時仍提供清理功能。
if (!window.trustedTypes) {
window.trustedTypes = {
createPolicy: (name, rules) => rules,
};
}
這樣一來,您現在就能使用 Trusted Types 模式編寫程式碼。支援的瀏覽器會強制執行類型,而舊版瀏覽器仍會透過模擬政策執行清理邏輯。
如果瀏覽器不支援可信任類型,則會將 TrustedHTML 物件視為字串 (如果使用 tinyfill),或不會在接收器層級執行強制檢查。