DOMPurify 是一款僅適用於 DOM、超快、超級容忍 XSS 的 HTML、MathML 和 SVG 清理程式。
它的使用和上手也非常簡單。 DOMPurify 於 2014 年 2 月啟動,同時版本已達v3.2.1 。
DOMPurify 使用 JavaScript 編寫,適用於所有現代瀏覽器(Safari (10+)、Opera (15+)、Edge、Firefox 和 Chrome - 以及幾乎所有使用 Blink、Gecko 或 WebKit 的其他瀏覽器)。它不會在 MSIE 或其他舊版瀏覽器上中斷。它根本什麼都不做。
請注意,DOMPurify v2.5.7 是支援 MSIE 的最新版本。對於與 MSIE 相容的重要安全性更新,請使用 2.x 分支。
我們的自動化測試目前涵蓋 24 種不同的瀏覽器,未來還會有更多瀏覽器。我們也介紹了在 jsdom 上執行 DOMPurify 的 Node.js v16.x、v17.x、v18.x 和 v19.x。眾所周知,舊的 Node 版本也可以工作,但嘿......不能保證。
DOMPurify 是由在 Web 攻擊和 XSS 方面擁有豐富背景的安全人員編寫的。不要害怕。有關更多詳細信息,請另閱讀我們的安全目標和威脅模型。請閱讀它。就像,真的。
DOMPurify 可以淨化 HTML 並防止 XSS 攻擊。您可以向 DOMPurify 提供充滿髒 HTML 的字串,它將傳回一個包含乾淨 HTML 的字串(除非另有配置)。 DOMPurify 將刪除所有包含危險 HTML 的內容,從而防止 XSS 攻擊和其他惡意行為。這也太快了。我們使用瀏覽器提供的技術並將其變成 XSS 過濾器。您的瀏覽器速度越快,DOMPurify 的速度就越快。
這很容易。只需在您的網站上包含 DOMPurify 即可。
<script type="text/javascript" src="src/purify.js"></script>
<script type="text/javascript" src="dist/purify.min.js"></script>
之後,您可以透過執行以下程式碼來清理字串:
const clean = DOMPurify.sanitize(dirty);
或者也許是這樣,如果你喜歡使用 Angular 或類似的東西:
import DOMPurify from 'dompurify';const clean = DOMPurify.sanitize('<b>你好</b>');
可以使用innerHTML
將產生的HTML寫入DOM元素,或是使用document.write()
將產生的HTML寫入DOM。這完全取決於你。請注意,預設情況下,我們允許 HTML、SVG和MathML。如果您只需要 HTML(這可能是一個非常常見的用例),您也可以輕鬆進行設定:
const clean = DOMPurify.sanitize(dirty, { USE_PROFILES: { html: true } });
好吧,請注意,如果您先清理 HTML,然後再修改它,您可能很容易使清潔效果失效。如果您在清理後將清理後的標記提供給另一個庫,請確保該庫不會自行處理 HTML。
清理標記後,您還可以查看屬性DOMPurify.removed
並找出哪些元素和屬性被丟棄。請不要使用此屬性做出任何安全關鍵決策。這只是好奇心的一個小幫手。
DOMPurify 技術上也可以在伺服器端與 Node.js 一起使用。我們的支援努力遵循 Node.js 發布週期。
在伺服器上執行 DOMPurify 需要存在 DOM,這可能並不奇怪。通常,jsdom 是首選工具,我們強烈建議使用最新版本的jsdom 。
為什麼?因為眾所周知,舊版的jsdom有問題,即使DOMPurify 100% 正確地完成所有操作,也會導致 XSS。例如, jsdom v19.0.0中存在已知的攻擊向量,這些攻擊向量已在jsdom v20.0.0中修復 - 因此,我們確實建議保持jsdom最新。
另請注意,像 happy-dom 這樣的工具是存在的,但目前並不被認為是安全的。目前不建議將 DOMPurify 與happy-dom結合使用,這可能會導致 XSS。
除此之外,您可以在伺服器上使用 DOMPurify。大概。這實際上取決於jsdom或您在伺服器端使用的任何 DOM。如果您可以接受這一點,那麼您可以透過以下方式讓它發揮作用:
npm 安裝 dompurify npm 安裝 jsdom
對於jsdom (請使用最新版本),這應該可以解決問題:
const createDOMPurify = require('dompurify');const { JSDOM } = require('jsdom');const window = new JSDOM('').window;const DOMPurify = createDOMPurify(window);const clean = DOMPurify.sanitize(' <b>你好</b>');
或者甚至是這樣,如果您更喜歡使用導入:
import { JSDOM } from 'jsdom';import DOMPurify from 'dompurify';const window = new JSDOM('').window;const purify = DOMPurify(window);const clean = purify.sanitize('<b>你好<你好/b>');
如果您在特定設定中遇到問題,請考慮查看令人驚嘆的 isomorphic-dompurify 項目,它解決了人們可能遇到的許多問題。
npm 安裝同構-dompurify
import DOMPurify from 'isomorphic-dompurify';const clean = DOMPurify.sanitize('<s>hello</s>');
當然有演示!玩 DOMPurify
首先,請立即透過電子郵件與我們聯繫,以便我們進行修復。 PGP金鑰
此外,您可能有資格獲得錯誤賞金! Fastmail 的優秀人員使用 DOMPurify 提供服務,並將我們的庫添加到他們的錯誤賞金範圍中。因此,如果您找到繞過或削弱 DOMPurify 的方法,請同時查看他們的網站和錯誤賞金資訊。
純化標記是什麼樣子的?好吧,演示展示了一大堆令人討厭的元素。但我們也展示一些較小的例子!
DOMPurify.sanitize('<img src=x onerror=alert(1)//>'); // 變成 <img src="x">DOMPurify.sanitize('<svg><g/onload=alert(2)//<p>'); // 變成 <svg><g></g></svg>DOMPurify.sanitize('<p>abc<iframe//src=jAva	script:alert(3)>def</p>'); // 變成 <p>abc</p>DOMPurify.sanitize('<math><mi//xlink:href="data:x,<script>alert(4)</script>">'); // 變成 <math><mi></mi></math>DOMPurify.sanitize('<TABLE><tr><td>HELLO</tr></TABL>'); // 變成<table><tbody><tr><td>HELLO</td></tr></tbody></table>DOMPurify.sanitize('<UL><li><A HREF=// google .com>點選</UL>'); // 變成 <ul><li><a href="//google.com">點選</a></li></ul>
DOMPurify 目前支援 HTML5、SVG 和 MathML。 DOMPurify 預設允許 CSS、HTML 自訂資料屬性。 DOMPurify 也支援 Shadow DOM - 並遞歸地清理 DOM 模板。 DOMPurify 還允許您清理 HTML,以便與 jQuery $()
和elm.html()
API 一起使用,而不會出現任何已知問題。
DOMPurify 根本不會執行任何操作。它只是返回您輸入的字串。 DOMPurify 公開了一個名為isSupported
屬性,它告訴您它是否能夠完成其工作,以便您可以製定自己的備份計劃。
在 1.0.9 版本中,DOMPurify 新增了對可信任類型 API 的支援。在版本 2.0.0 中,新增了一個設定標誌來控制 DOMPurify 的相關行為。
當DOMPurify.sanitize
在可信任類型 API 可用且RETURN_TRUSTED_TYPE
設定為true
環境中使用時,它會嘗試傳回TrustedHTML
值而不是字串( RETURN_DOM
和RETURN_DOM_FRAGMENT
設定選項的行為不會改變)。
請注意,為了使用 DOMPurify 在trustedTypes
中建立策略,需要RETURN_TRUSTED_TYPE: false
,因為createHTML
需要普通字串,而不是TrustedHTML
。下面的例子展示了這一點。
window.trustedTypes!.createPolicy('預設', { createHTML: (to_escape) =>DOMPurify.sanitize(to_escape, { RETURN_TRUSTED_TYPE: false }),});
是的。包含的預設配置值已經相當不錯了 - 但您當然可以覆蓋它們。查看/demos
資料夾,查看大量有關如何自訂 DOMPurify 的範例。
// 剝離 {{ ... }}、${ ... } 和 <% ... %> 以使輸出對模板系統安全 // 請小心,不建議在生產環境中使用此模式。完全不建議在使用者控制的HTML 中進行模板解析。 // 僅在確實沒有替代方案時才使用此模式。的方式HTML 字元被處理。 // 否則,從 `true` 改為 `false` 將會以這種或其他方式導致 XSS。
// 只允許<b> 元素,非常嚴格const clean = DOMPurify.sanitize(dirty, {ALLOWED_TAGS: ['b']});// 只允許有樣式屬性的<b> 和<q> const clean = DOMPurify.sanitize( dirty, {ALLOWED_TAGS: ['b', 'q'], ALLOWED_ATTR: ['style']});// 允許所有安全HTML 元素,但SVG 和MathML 都不允許// 請注意,USE_PROFILES設定將覆蓋ALLOWED_TAGS 設定/ / 所以不要一起使用它們const clean = DOMPurify.sanitize(dirty, {USE_PROFILES: {html: true}});// 允許所有安全的SVG 元素和SVG 過濾器,不允許HTML 或MathML const clean = DOMPurify.sanitize(dirty, {USE_PROFILES: {svg: true, svgFilters: true}});// 允許所有安全的MathML 元素和SVG,但不允許SVG 過濾器const clean = DOMPurify.sanitize(dirty, {USE_PROFILES: {mathMl: true, svg: true}} );// 將預設命名空間從HTML 更改為不同的內容const clean = DOMPurify.sanitize(dirty, {NAMESPACE: 'http://www.w3.org /2000/svg'});// 保留所有安全HTML並將<style> 元素加入到區塊清單const clean = DOMPurify.sanitize(dirty, {FORBID_TAGS: ['style']});//保留所有安全HTML不變,並將樣式屬性新增至區塊清單const clean = DOMPurify .sanitize(dirty, {FORBID_ATTR: ['style']});// 擴展現有的允許標籤數組並將<my-tag> 新增至允許列表const clean = DOMPurify.sanitize(dirty, {ADD_TAGS: ['my -tag']});// 擴展現有的允許屬性數組,並將my-attr 新增至允許列表const clean = DOMPurify.sanitize(dirty , {ADD_ATTR: ['my-attr']});// 禁止ARIA屬性,保留其他安全HTML 不變(預設為true)const clean = DOMPurify.sanitize(dirty, {ALLOW_ARIA_ATTR: false});// 禁止HTML5 資料屬性,保留其他安全HTML 不變(預設為true)const clean = DOMPurify.sanitize(髒, {ALLOW_DATA_ATTR: false});
// DOMPurify 允許定義自訂元素的規則。當使用 CUSTOM_ELEMENT_HANDLING// 文字時,可以準確地定義您想要允許的元素(預設情況下,不允許任何元素)。預設情況下,使用內建或設定的allow.list。過。小心處理! : {tagNameCheck: null, // 不允許使用自訂元素attributeNameCheck: null, // 使用預設/標準屬性允許清單allowCustomizedBuiltInElements: false, // 不允許使用自訂內建元素},}); // <div is=""></div>const clean = DOMPurify.sanitize('<foo-bar baz="foobar"禁忌="true"></foo-bar><div is="foo-baz "></div>',{CUSTOM_ELEMENT_HANDLING: {tagNameCheck: /^foo-/, // 允許所有以「foo-」開頭的標籤attributeNameCheck: /baz/, // 允許所有包含「baz」的屬性allowCustomizedBuiltInElements: true, // 允許自訂內建函數},}); // <foo-bar baz="foobar"></foo-bar><div is="foo-baz"></div>const clean = DOMPurify.sanitize('<foo-bar baz="foobar" 禁止="true"></foo-bar><div is="foo-baz"></div>',{CUSTOM_ELEMENT_HANDLING: {tagNameCheck: (tagName) => tagName.match(/^foo-/), / / 允許所有以「foo-」開頭的標籤attributeNameCheck: (attr) => attr.match(/baz/), // 允許所有包含「baz」的標籤allowCustomizedBuiltInElements: true, // 允許自訂內建},} ); // <foo-bar baz="foobar"></foo-bar><div is="foo-baz"></div>
// 擴充可以使用 Data URIs 的現有元素數組 const clean = DOMPurify.sanitize(dirty, {ADD_DATA_URI_TAGS: ['a', 'area']});// 擴充對 URI 安全性的現有元素數組-像值(小心,XSS 風險)const clean = DOMPurify.sanitize(dirty, {ADD_URI_SAFE_ATTR: ['my-attr']});
// 在URL 屬性中允許外部協定處理程序(預設為false,小心,XSS 風險)// 預設僅允許http、https、ftp、ftps、tel、mailto、callto、sms、cid 和xmpp。 = DOMPurify.sanitize(dirty, {ALLOW_UNKNOWN_PROTOCOLS: true});// 透過正規表示式允許URL 屬性中的特定協定處理程序(預設為false,小心,XSS 風險)// 預設情況下僅http、https、ftp 、ftps、tel、允許mailto、callto、sms、cid 和xmpp。 // 預設正規表示式: /^(?:(?:(?:f|ht)tps?|mailto|tel|callto|sms|cid |xmpp):|[ ^az]|[a-z+.-]+(?:[^a-z+.-:]|$))/i;const clean = DOMPurify.sanitize(髒, {ALLOWED_URI_REGEXP: / ^(?:(? :(?:f|ht)tps?|mailto|tel|callto|sms|cid|xmpp|xxx):|[^az]|[a-z+.-]+(?:[ ^a-z+.- :]|$))/i});
// 傳回 DOM HTMLBodyElement 而不是 HTML 字串(預設為 false)const clean = DOMPurify.sanitize(dirty, {RETURN_DOM: true});// 傳回 DOM DocumentFragment 而不是 HTML 字串(預設為 false)const clean = DOMPurify .sanitize(dirty, {RETURN_DOM_FRAGMENT: true});// 使用RETURN_TRUSTED_TYPE 標誌開啟可信任類型支援(如果可用) const clean = DOMPurify.sanitize(dirty, {RETURN_TRUSTED_TYPE: true}); // 如果可能,將傳回TrustedHTML 物件而非字串// 使用提供的可信任類型策略const clean = DOMPurify.sanitize(dirty, {// 提供的策略必須定義createHTML 和createScriptURLTRUSTED_TYPES_POLICY: trustTypes.createPolicy({createHTML_TYPES_POLICY: trustTypes.createPolicy({createHTML_TYPES_POLICY: trustTypes.createPolicy({createHTML_TYPES_POLICY: trustTypes.createPolicy({createHTML_TYPES_POLICY: trustTypes.createPolic s) {返回 s},createScriptURL(s) { 返回 s},}});
// 傳回包含<html> 標籤的整個文件(預設為false)const clean = DOMPurify.sanitize(dirty, {WHOLE_DOCUMENT: true});// 停用輸出上的DOM Clobbering 保護(預設為true,小心處理,輕微XSS此處有風險)const clean = DOMPurify.sanitize(dirty, {SANITIZE_DOM: false});// 透過命名空間隔離強制實施嚴格的DOM 破壞保護(預設為false)// 啟用後,隔離命名屬性的命名空間(即`id ` 和`name` 屬性)// 來自JS 變量,透過在它們前面加上字串`user-content-`const clean = DOMPurify.sanitize(dirty, {SANITIZE_NAMED_PROPS: true});// 當元素被刪除時保留元素的內容已刪除(預設為true)const clean = DOMPurify.sanitize(dirty, {KEEP_CONTENT: false});// 將樣式、腳本或其他元素黏合到document.body 並防止在幾種邊緣情況下出現不直觀的瀏覽器行為(預設為false )const clean = DOMPurify.sanitize(dirty, {FORCE_BODY: true});// 刪除已刪除的<p> 元素下的所有<a> 元素const clean = DOMPurify.sanitize(dirty, {FORBID_CONTENTS: ['a'], FORBID_TAGS: ['p']});// 更改解析器類型,以便將清理後的資料視為XML 而不是HTML,這是預設值const clean = DOMPurify.sanitize(dirty, {PARSER_MEDIA_TYPE: 'application/xhtml+xml' });
// 使用IN_PLACE 模式「就地」清理節點,這要快得多,具體取決於您如何使用DOMPurifyconst dirty = document.createElement('a');dirty.setAttribute('href', 'javascript:alert(1 ) )');const clean = DOMPurify.sanitize(dirty, {IN_PLACE: true}); // 請參閱 https://github.com/cure53/DOMPurify/issues/288 以了解更多信息
這裡還有更多範例,展示如何運作、自訂和配置 DOMPurify 以滿足您的需求。
您可以使用DOMPurify.setConfig
方法,而不是重複將相同的設定傳遞給DOMPurify.sanitize
。您的配置將持續存在,直到您下次呼叫DOMPurify.setConfig
或呼叫DOMPurify.clearConfig
來重置它為止。請記住,只有一個活動配置,這意味著一旦設置,傳遞給DOMPurify.sanitize
所有額外配置參數都會被忽略。
DOMPurify 讓您可以透過使用DOMPurify.addHook
方法將一個或多個函數附加到以下掛鉤之一來增強其功能:
beforeSanitizeElements
uponSanitizeElement
(No 's' - 為每個元素呼叫)
afterSanitizeElements
beforeSanitizeAttributes
uponSanitizeAttribute
afterSanitizeAttributes
beforeSanitizeShadowDOM
uponSanitizeShadowNode
afterSanitizeShadowDOM
它將目前處理的 DOM 節點傳遞給回調,並在需要時傳遞帶有已驗證節點和屬性資料的文字以及 DOMPurify 配置。請參閱 MentalJS hook 演示,了解如何善用該 API。
例子:
DOMPurify.addHook( 'uponSanitizeAttribute', function (currentNode, hookEvent, config) {// 對目前節點執行某些操作// 您也可以改變目前節點的hookEvent(即設定hookEvent.forceKeepAttr = true)// 對於'uponSanitizeAttribute' 以外的鉤子類型hookEvent 等於null });
選項 | 自從 | 筆記 |
---|---|---|
SAFE_FOR_JQUERY | 2.1.0 | 無需更換。 |
我們目前正在將 Github Actions 與 BrowserStack 結合使用。這使我們能夠確認每一次提交都在所有受支援的瀏覽器中按計劃進行。在此處查看建置日誌:https://github.com/cure53/DOMPurify/actions
您可以透過執行npm test
進一步執行本機測試。測試在 Node.js v0.6.2 和 [email protected] 上運作良好。
所有相關提交都將使用金鑰0x24BB6BF4
進行簽名,以提高安全性(自 2016 年 4 月 8 日起)。
npm i
)我們正式支持npm
。 GitHub Actions 工作流程配置為使用npm
安裝依賴項。當使用已棄用的npm
版本時,我們無法完全確保已安裝依賴項的版本,這可能會導致意外問題。
我們依靠 npm 運行腳本來與我們的工具基礎設施整合。我們使用 ESLint 作為預先提交掛鉤來確保程式碼一致性。此外,為了簡化格式化,我們在透過rollup
建置/dist
資產時使用 prettier 。
這些是我們的 npm 腳本:
npm run dev
開始構建,同時觀察原始碼的變化
npm run test
透過 jsdom 和 karma 來運行我們的測試套件
test:jsdom
僅透過 jsdom 執行測試
test:karma
僅透過 karma 執行測試
npm run lint
使用 ESLint 對原始程式碼進行 lint(透過 xo)
npm run format
使用 prettier 來格式化我們的原始程式碼以輕鬆透過 ESLint
npm run build
將我們的分發資產建構成壓縮和未壓縮的 UMD 模組
npm run build:umd
僅建構未縮小的 UMD 模組
npm run build:umd:min
僅建構縮小的 UMD 模組
注意:所有運行腳本都是透過npm run <script>
觸發的。
還有更多的 npm 腳本,但它們主要是為了與 CI 集成,或者是“私有的”,例如在每次提交時修改構建分發文件。
我們媽