Trusted Types เป็นฟีเจอร์ความปลอดภัยที่ช่วยป้องกัน Cross-Site Scripting ที่อิงตาม DOM (DOM XSS) โดยกำหนดให้ข้อมูลที่ส่งไปยัง API ของเบราว์เซอร์ที่ "เป็นอันตราย" (หรือที่เรียกว่าซิงก์) ต้องแปลงเป็นประเภทที่เฉพาะเจาะจงก่อน เช่น TrustedHTML
โดยค่าเริ่มต้น DOM API จำนวนมาก (เช่น innerHTML หรือ document.write) จะยอมรับสตริงดิบ หากสตริงมีสคริปต์ที่เป็นอันตรายจากแหล่งที่มาที่ไม่น่าเชื่อถือ ระบบจะเรียกใช้สคริปต์ดังกล่าวได้ Trusted Types จะเปลี่ยนลักษณะการทำงานนี้ โดยเมื่อเปิดใช้ผ่าน CSP แล้ว ซิงก์เหล่านี้จะยอมรับเฉพาะออบเจ็กต์ TrustedHTML ที่สร้างโดยนโยบายที่ได้รับอนุญาตเท่านั้น
รายละเอียดการติดตั้งใช้งานที่สำคัญ
หากต้องการใช้ Trusted Types ให้ทำตามขั้นตอนหลัก 4 ขั้นตอนต่อไปนี้
- กำหนดนโยบายความปลอดภัยที่ระบุวิธีทำความสะอาดหรือเปลี่ยนรูปแบบสตริง
- ใช้นโยบายดังกล่าวเพื่อสร้างออบเจ็กต์ประเภทที่เชื่อถือได้จากสตริงดิบ
- ใช้ออบเจ็กต์ประเภทที่เชื่อถือได้เมื่อเขียนไปยังซิงก์ DOM
- บังคับใช้ประเภทที่เชื่อถือได้โดยใช้นโยบายรักษาความปลอดภัยเนื้อหา (CSP)
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 Types
อัปเดตแต่ละอินสแตนซ์ที่คุณเขียนไปยังซิงก์เพื่อแปลงเป็นออบเจ็กต์ประเภทที่เชื่อถือได้ก่อน หากต้องการช่วยค้นหาตำแหน่งที่ต้องอัปเดตโค้ด คุณสามารถกำหนดนโยบาย 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 ที่ไม่น่าเชื่อถือ) ไม่จำเป็นต้องใช้นโยบายประเภทที่เชื่อถือได้ เนื่องจากเมธอดเหล่านี้จะทำความสะอาดการโจมตี XSS ที่อาจเกิดขึ้นโดยค่าเริ่มต้นเสมอ
4. การบังคับใช้ Trusted Types ด้วย CSP
ควรเปิดตัว Trusted Types แบบค่อยเป็นค่อยไปเพื่อไม่ให้แอปพลิเคชันขัดข้อง แม้ว่าการบังคับใช้เต็มรูปแบบจะให้การปกป้องที่รัดกุมที่สุด แต่ก็มีความเสี่ยงที่เว็บไซต์จะเสียหายหากไม่ได้ย้ายข้อมูลการเขียน Sink ทั้งหมด
ข้อกำหนดเบื้องต้น
- การทำงานร่วมกันของเฟรมเวิร์ก: หากเฟรมเวิร์กของแอปพลิเคชัน (หรือไลบรารีของบุคคลที่สามที่เขียนไปยังซิงก์ DOM) ไม่สร้างค่า
TrustedHTMLหรือTrustedScriptคุณจะบังคับใช้ประเภทที่เชื่อถือได้ไม่ได้โดยไม่ทำให้โค้ดนั้นเสียหาย ตรวจสอบการรองรับเฟรมเวิร์กก่อนเริ่มการเปิดตัว - การเปลี่ยนโครงสร้างภายในโค้ดของซิงก์: ตรวจสอบว่าคุณได้ระบุและเปลี่ยนโครงสร้างภายในโค้ดของซิงก์ที่เป็นอันตรายเพื่อใช้นโยบายประเภทที่เชื่อถือได้
กลยุทธ์การเปิดตัว
รายงานเท่านั้น: ขั้นแรก ให้เปิดตัวโดยใช้ส่วนหัว
Content-Security-Policy-Report-Onlyซึ่งจะบันทึกการละเมิดไปยังปลายทางการรายงานโดยไม่บล็อกการเขียนจริงๆContent-Security-Policy-Report-Only: require-trusted-types-for 'script';การบังคับใช้: เมื่อคุณผสานรวมนโยบายและรายงานการละเมิดลดลงเหลือ 0 แล้ว ให้เปลี่ยนไปใช้การบังคับใช้อย่างเต็มรูปแบบ
Content-Security-Policy: require-trusted-types-for 'script';
นอกจากนี้ คุณยังจำกัดนโยบายที่อนุญาตให้สร้างได้ (ระบุชื่อนโยบาย เช่น sanitize-html หรือ escape-html, รวม dompurify หากนโยบายมอบสิทธิ์ให้ DOMPurify.sanitize() ซึ่งจะสร้างนโยบาย dompurify ภายใน และรวม default หากคุณลงทะเบียนนโยบายเริ่มต้นระหว่างการย้ายข้อมูล)
Content-Security-Policy: require-trusted-types-for 'script'; trusted-types sanitize-html escape-html dompurify;
เมื่อใช้ CSP นี้ การเขียนไปยังซิงก์ที่ไม่ได้ใช้ประเภทที่เชื่อถือได้จะทำให้เกิดข้อผิดพลาด นอกจากนี้ การพยายามสร้างนโยบายประเภทที่เชื่อถือได้ซึ่งมีชื่อที่ไม่ได้ระบุไว้ใน trusted-types จะทำให้เกิดข้อผิดพลาด
นอกจากนี้ คุณยังตั้งค่า CSP นี้เป็นแท็ก <meta> ใน <head> ของเอกสารได้ด้วย แม้ว่าวิธีนี้อาจมีความปลอดภัยน้อยกว่าก็ตาม
<!-- 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;"
/>
กลยุทธ์สำรอง
สำหรับเบราว์เซอร์ที่ยังไม่รองรับ Trusted Types คุณสามารถใช้ "tinyfill" ขนาดเล็กเพื่อให้แน่ใจว่าโค้ดจะไม่เสียหายขณะที่ยังคงทำการตรวจสอบความถูกต้องอยู่
if (!window.trustedTypes) {
window.trustedTypes = {
createPolicy: (name, rules) => rules,
};
}
ซึ่งจะช่วยให้คุณเขียนโค้ดโดยใช้รูปแบบ Trusted Types ได้ตั้งแต่วันนี้ เบราว์เซอร์ที่รองรับจะบังคับใช้ประเภทต่างๆ ส่วนเบราว์เซอร์รุ่นเก่าจะยังคงเรียกใช้ตรรกะการล้างข้อมูลผ่านนโยบายจำลอง
หากเบราว์เซอร์ไม่รองรับประเภทที่เชื่อถือได้ เบราว์เซอร์จะถือว่าออบเจ็กต์ TrustedHTML เป็นสตริง (หากใช้ Tinyfill) หรือจะไม่มีการตรวจสอบการบังคับใช้ที่ระดับซิงก์