運用 Trusted Types 防範 DOM 型跨網站指令碼攻擊安全漏洞

Krzysztof Kotowicz
Krzysztof Kotowicz

瀏覽器支援

  • 83
  • 83
  • x
  • x

資料來源

DOM 型跨網站指令碼攻擊 (DOM XSS) 是指來自使用者控制的來源資料 (例如使用者名稱,或從網址片段取得的重新導向網址) 的資料到達「接收器」。這個函式是指 eval() 的函式,或 .innerHTML 這類可執行任意 JavaScript 程式碼的屬性集器。

DOM XSS 是最常見的網路安全漏洞之一,開發團隊常會不小心在應用程式中導入這類安全漏洞。受信任的類型提供各項工具,可用於編寫、安全性審查,以及確保危險的網路 API 功能預設安全無虞,讓應用程式不受 DOM XSS 安全漏洞的影響。信任類型可做為 polyfill (在不支援這些類型的瀏覽器中)。

背景

DOM XSS 多年來一直是最常見且最危險的網路安全性漏洞之一。

跨網站指令碼攻擊有兩種,某些 XSS 安全漏洞,是因為伺服器端程式碼以不安全的方式建立構成網站的 HTML 程式碼。有些則是用戶端上的根本原因,因為 JavaScript 程式碼會透過使用者控制的內容呼叫危險函式。

如要防止伺服器端 XSS,請勿以串連字串的方式產生 HTML。如要解決其他錯誤,請改用安全的內容自動封裝範本程式庫,並搭配以 Nonce 為基礎的內容安全政策

現在,瀏覽器也可以使用信任類型,協助避免用戶端 DOM 式 XSS。

API 簡介

會鎖定下列有風險的接收器功能,藉此信任受信任的類型。您或許已經認識其中一些技術,因為瀏覽器廠商和網路架構已經中止您使用這些功能,但安全性考量。

受信任類型會要求您先處理資料,再將其傳送至這些接收器函式。僅使用字串失敗,因為瀏覽器不知道資料是否可信:

錯誤做法
anElement.innerHTML  = location.href;
啟用信任類型後,瀏覽器會擲回 TypeError,並禁止使用含有字串的 DOM XSS 接收器。

若要表示資料已安全處理,請建立一個特殊物件「Trusted Type」。

正確做法
anElement.innerHTML = aTrustedHTML;
  
啟用信任類型後,瀏覽器就會接受 TrustedHTML 物件,用於接收預期 HTML 程式碼片段的接收器。其他機密接收器也有 TrustedScriptTrustedScriptURL 物件。

受信任的類型可大幅減少應用程式的 DOM XSS 攻擊面。這可以簡化安全性審查,並讓您在執行階段中編譯、檢查或繫結程式碼時,強制執行以類型為基礎的安全性檢查。

如何使用信任類型

準備檢舉內容安全政策違反事件

您可以部署報表收集器 (例如開放原始碼 go-csp-collector),或使用其中一種商用對等項目。您也可以在瀏覽器中對違規問題進行偵錯:

document.addEventListener('securitypolicyviolation',
    console.error.bind(console));

新增報表專用的 CSP 標頭

將下列 HTTP 回應標頭新增至要遷移至受信任類型的文件:

Content-Security-Policy-Report-Only: require-trusted-types-for 'script'; report-uri //my-csp-endpoint.example

現在所有違規事件都會回報給 //my-csp-endpoint.example,但網站仍會繼續運作。下一節將說明 //my-csp-endpoint.example 的運作方式。

找出可信類型的違規行為

從現在起,每次信任的類型偵測到違規時,瀏覽器都會將報告傳送到設定的 report-uri。舉例來說,當應用程式將字串傳送至 innerHTML 時,瀏覽器就會傳送下列報表:

{
"csp-report": {
    "document-uri": "https://my.url.example",
    "violated-directive": "require-trusted-types-for",
    "disposition": "report",
    "blocked-uri": "trusted-types-sink",
    "line-number": 39,
    "column-number": 12,
    "source-file": "https://my.url.example/script.js",
    "status-code": 0,
    "script-sample": "Element innerHTML <img src=x"
}
}

這表示在第 39 行的 https://my.url.example/script.js 中,系統使用以 <img src=x 開頭的字串呼叫 innerHTML。這項資訊可協助您找出程式碼可能會導入 DOM XSS 且需要變更的部分。

修正違規問題

有幾種方式可以修正「Trusted Type」違規問題。您可以移除違規程式碼使用程式庫建立可信類型政策,也可以建立預設政策,做為最後的方法。

改寫違規的程式碼

這可能是因為不再需要不符合規定的程式碼,也可能不使用造成違規的函式重新編寫:

正確做法
el.textContent = '';
const img = document.createElement('img');
img.src = 'xyz.jpg';
el.appendChild(img);
錯誤做法
el.innerHTML = '';

使用程式庫

部分程式庫已產生可信類型,方便您傳遞至接收器函式。例如,您可以使用 DOMPurify 來淨化 HTML 程式碼片段,並移除 XSS 酬載。

import DOMPurify from 'dompurify';
el.innerHTML = DOMPurify.sanitize(html, {RETURN_TRUSTED_TYPE: true});

DOMPurify 支援 Trusted Types,並傳回包裝在 TrustedHTML 物件中的經過處理的 HTML,因此瀏覽器不會產生違規事件。

建立可信類型政策

有時候,您無法移除造成違規的程式碼,且也沒有程式庫可為您清理值並建立可信類型。在這種情況下,您可以自行建立 Trusted Type 物件。

首先請建立政策。 政策是受信任的類型,其會在輸入時強制執行特定安全性規則:

if (window.trustedTypes && trustedTypes.createPolicy) { // Feature testing
  const escapeHTMLPolicy = trustedTypes.createPolicy('myEscapePolicy', {
    createHTML: string => string.replace(/\</g, '&lt;')
  });
}

此程式碼會建立名為 myEscapePolicy 的政策,該政策可使用其 createHTML() 函式產生 TrustedHTML 物件。定義規則 HTML 逸出 < 字元可避免建立新的 HTML 元素。

請使用以下政策:

const escaped = escapeHTMLPolicy.createHTML('<img src=x onerror=alert(1)>');
console.log(escaped instanceof TrustedHTML);  // true
el.innerHTML = escaped;  // '&lt;img src=x onerror=alert(1)>'

使用預設政策

有時您無法變更違規程式碼,例如從 CDN 載入第三方程式庫時。在這種情況下,請使用預設政策

if (window.trustedTypes && trustedTypes.createPolicy) { // Feature testing
  trustedTypes.createPolicy('default', {
    createHTML: (string, sink) => DOMPurify.sanitize(string, {RETURN_TRUSTED_TYPE: true})
  });
}

如果在只接受受信任類型的接收器中使用字串,則系統會使用名為 default 的政策。

改為強制執行內容安全政策

當應用程式不再產生違規事件時,您可以開始強制執行信任的類型:

Content-Security-Policy: require-trusted-types-for 'script'; report-uri //my-csp-endpoint.example

現在,無論您的網頁應用程式多麼複雜,只要其中一項政策都能導入 DOM XSS 安全漏洞,即須限制政策建立

其他資訊