levmyshkin / dom_purify
DOMPurify是一个仅使用DOM的、超级快速、极具容忍度的XSS清理器,适用于HTML、MathML和SVG
This package is not auto-updated.
Last update: 2024-09-25 18:44:46 UTC
README
DOMPurify是一个仅使用DOM的、超级快速、极具容忍度的XSS清理器,适用于HTML、MathML和SVG。
它也非常简单易用和入门。DOMPurify始于2014年2月,同时,已达到版本2.4.1。
DOMPurify是用JavaScript编写的,并在所有现代浏览器中运行(Safari(10+)、Opera(15+)、Internet Explorer(10+)、Edge、Firefox和Chrome - 以及几乎所有使用Blink或WebKit的浏览器)。它不会在MSIE6或其他旧浏览器上中断。它要么使用回退,要么什么也不做。
我们的自动化测试覆盖了19种不同的浏览器,还有更多。我们还涵盖了Node.js v14.x、v16.x、v17.x和v18.x,在jsdom上运行DOMPurify。已知的旧Node版本也可以正常工作,但...没有保证。
DOMPurify是由有广泛网络攻击和XSS背景的安全人员编写的。不用担心。有关更多详情,请阅读我们的安全目标 & 威胁模型。请阅读它。真的。
它做什么?
DOMPurify清理HTML并防止XSS攻击。您可以将充满脏HTML的字符串输入DOMPurify,它将返回一个字符串(除非配置了其他方式),其中包含干净的HTML。DOMPurify将删除所有包含危险HTML的内容,从而防止XSS攻击和其他恶意行为。它也非常快。我们使用浏览器提供的功能,将其转换为XSS过滤器。您的浏览器越快,DOMPurify就越快。
如何使用它?
很简单。只需在您的网站上包含DOMPurify即可。
使用未压缩的开发版本
<script type="text/javascript" src="src/purify.js"></script>
使用压缩并经过测试的生产版本(提供source-map)
<script type="text/javascript" src="dist/purify.min.js"></script>
之后,您可以通过执行以下代码来清理字符串
let clean = DOMPurify.sanitize(dirty);
或者,如果您喜欢使用Angular等
import * as DOMPurify from 'dompurify'; let clean = DOMPurify.sanitize('<b>hello there</b>');
结果HTML可以使用innerHTML
或DOM的document.write()
写入。这完全取决于您。请注意,默认情况下,我们允许HTML、SVG 和 MathML。如果您只需要HTML,这可能是一个非常常见的用例,您也可以轻松设置
let clean = DOMPurify.sanitize(dirty, { USE_PROFILES: { html: true } });
TypeScript类型定义在哪里?
它们可以在这里找到:@types/dompurify
是否存在任何潜在的危险?
请注意,如果您首先清理HTML,然后修改它,您可能会轻易地无效化清理效果。如果您在清理后将标记传递给另一个库,请确保该库不会自行更改HTML。
好吧,有道理,让我们继续。
在清理您的标记后,您还可以查看属性 DOMPurify.removed
并了解哪些元素和属性被排除。请不要使用此属性做出任何安全关键的决定。这只是一个为好奇心强的人提供的辅助工具。
在服务器上运行 DOMPurify
DOMPurify在技术上也可以与Node.js一起在服务器端运行。我们的支持努力遵循Node.js版本周期。
在服务器上运行DOMPurify需要存在一个DOM,这可能是预料之中的。通常,jsdom是首选工具,我们强烈建议使用jsdom的最新版本。
为什么?因为已知较旧的jsdom版本存在一些可能导致XSS的问题,即使DOMPurify做得100%正确。例如,在jsdom v19.0.0中存在已知的攻击向量,这些攻击向量在jsdom v20.0.0中得到修复——因此我们真的建议为了这个原因而保持jsdom的最新状态。
除此之外,您可以在服务器上使用DOMPurify。可能吧。这实际上取决于您在服务器端使用的jsdom或任何DOM。如果您能接受这一点,以下是使其工作的方式
npm install dompurify npm install jsdom
对于jsdom(请使用最新版本),这应该会有效
const createDOMPurify = require('dompurify'); const { JSDOM } = require('jsdom'); const window = new JSDOM('').window; const DOMPurify = createDOMPurify(window); const clean = DOMPurify.sanitize('<b>hello there</b>');
或者如果您喜欢使用导入,甚至可以这样做
import { JSDOM } from 'jsdom'; import DOMPurify from 'dompurify'; const window = new JSDOM('').window; const purify = DOMPurify(window); const clean = purify.sanitize('<b>hello there</b>');
如果您在自己的特定设置中遇到问题,请考虑查看令人惊叹的isomorphic-dompurify项目,该项目解决了人们可能遇到的大量问题。
npm install isomorphic-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)//>'); // becomes <img src="x"> DOMPurify.sanitize('<svg><g/onload=alert(2)//<p>'); // becomes <svg><g></g></svg> DOMPurify.sanitize('<p>abc<iframe//src=jAva	script:alert(3)>def</p>'); // becomes <p>abc</p> DOMPurify.sanitize('<math><mi//xlink:href="data:x,<script>alert(4)</script>">'); // becomes <math><mi></mi></math> DOMPurify.sanitize('<TABLE><tr><td>HELLO</tr></TABL>'); // becomes <table><tbody><tr><td>HELLO</td></tr></tbody></table> DOMPurify.sanitize('<UL><li><A HREF=//google.com>click</UL>'); // becomes <ul><li><a href="//google.com">click</a></li></ul>
支持哪些内容?
DOMPurify目前支持HTML5、SVG和MathML。DOMPurify默认允许CSS、HTML自定义数据属性。DOMPurify还支持Shadow DOM——并递归地净化DOM模板。DOMPurify还允许您使用jQuery $()
和elm.html()
API净化HTML,而不会出现任何已知问题。
那么关于较旧浏览器如MSIE8怎么办?
DOMPurify为较旧的MSIE浏览器提供了回退行为。它使用仅适用于MSIE的toStaticHTML
功能进行净化。但是请注意,在此回退模式下,以下所示的几乎所有配置标志都没有任何效果。您需要自己处理。
如果连toStaticHTML
也不支持,DOMPurify将什么也不做。它简单地返回您输入的原始字符串。
DOMPurify还公开了一个名为isSupported
的属性,它告诉您DOMPurify是否能够完成其工作。
关于DOMPurify和Trusted Types怎么办?
在版本1.0.9中,DOMPurify添加了对Trusted Types API的支持。在版本2.0.0中,添加了一个配置标志来控制DOMPurify对此的行为。
当在支持 Trusted Types API 的环境中使用 DOMPurify.sanitize
,并且将 RETURN_TRUSTED_TYPE
设置为 true
时,它会尝试返回一个 TrustedHTML
值而不是字符串(RETURN_DOM
和 RETURN_DOM_FRAGMENT
配置选项的行为不会改变)。
我可以配置 DOMPurify 吗?
可以。内置的默认配置值已经相当不错了——但你当然可以覆盖它们。查看 /demos
文件夹,了解如何自定义 DOMPurify 的示例。
/** * General settings */ // strip {{ ... }}, ${ ... } and <% ... %> to make output safe for template systems // be careful please, this mode is not recommended for production usage. // allowing template parsing in user-controlled HTML is not advised at all. // only use this mode if there is really no alternative. var clean = DOMPurify.sanitize(dirty, {SAFE_FOR_TEMPLATES: true}); /** * Control our allow-lists and block-lists */ // allow only <b> elements, very strict var clean = DOMPurify.sanitize(dirty, {ALLOWED_TAGS: ['b']}); // allow only <b> and <q> with style attributes var clean = DOMPurify.sanitize(dirty, {ALLOWED_TAGS: ['b', 'q'], ALLOWED_ATTR: ['style']}); // allow all safe HTML elements but neither SVG nor MathML // note that the USE_PROFILES setting will override the ALLOWED_TAGS setting // so don't use them together var clean = DOMPurify.sanitize(dirty, {USE_PROFILES: {html: true}}); // allow all safe SVG elements and SVG Filters, no HTML or MathML var clean = DOMPurify.sanitize(dirty, {USE_PROFILES: {svg: true, svgFilters: true}}); // allow all safe MathML elements and SVG, but no SVG Filters var clean = DOMPurify.sanitize(dirty, {USE_PROFILES: {mathMl: true, svg: true}}); // change the default namespace from HTML to something different var clean = DOMPurify.sanitize(dirty, {NAMESPACE: 'http://www.w3.org/2000/svg'}); // leave all safe HTML as it is and add <style> elements to block-list var clean = DOMPurify.sanitize(dirty, {FORBID_TAGS: ['style']}); // leave all safe HTML as it is and add style attributes to block-list var clean = DOMPurify.sanitize(dirty, {FORBID_ATTR: ['style']}); // extend the existing array of allowed tags and add <my-tag> to allow-list var clean = DOMPurify.sanitize(dirty, {ADD_TAGS: ['my-tag']}); // extend the existing array of allowed attributes and add my-attr to allow-list var clean = DOMPurify.sanitize(dirty, {ADD_ATTR: ['my-attr']}); // prohibit ARIA attributes, leave other safe HTML as is (default is true) var clean = DOMPurify.sanitize(dirty, {ALLOW_ARIA_ATTR: false}); // prohibit HTML5 data attributes, leave other safe HTML as is (default is true) var clean = DOMPurify.sanitize(dirty, {ALLOW_DATA_ATTR: false}); /** * Control behavior relating to Custom Elements */ // DOMPurify allows to define rules for Custom Elements. When using the CUSTOM_ELEMENT_HANDLING // literal, it is possible to define exactly what elements you wish to allow (by default, none are allowed). // // The same goes for their attributes. By default, the built-in or configured allow.list is used. // // You can use a RegExp literal to specify what is allowed or a predicate, examples for both can be seen below. // The default values are very restrictive to prevent accidental XSS bypasses. Handle with great care! var clean = DOMPurify.sanitize( '<foo-bar baz="foobar" forbidden="true"></foo-bar><div is="foo-baz"></div>', { CUSTOM_ELEMENT_HANDLING: { tagNameCheck: null, // no custom elements are allowed attributeNameCheck: null, // default / standard attribute allow-list is used allowCustomizedBuiltInElements: false, // no customized built-ins allowed }, } ); // <div is=""></div> var clean = DOMPurify.sanitize( '<foo-bar baz="foobar" forbidden="true"></foo-bar><div is="foo-baz"></div>', { CUSTOM_ELEMENT_HANDLING: { tagNameCheck: /^foo-/, // allow all tags starting with "foo-" attributeNameCheck: /baz/, // allow all attributes containing "baz" allowCustomizedBuiltInElements: true, // customized built-ins are allowed }, } ); // <foo-bar baz="foobar"></foo-bar><div is=""></div> var clean = DOMPurify.sanitize( '<foo-bar baz="foobar" forbidden="true"></foo-bar><div is="foo-baz"></div>', { CUSTOM_ELEMENT_HANDLING: { tagNameCheck: (tagName) => tagName.match(/^foo-/), // allow all tags starting with "foo-" attributeNameCheck: (attr) => attr.match(/baz/), // allow all containing "baz" allowCustomizedBuiltInElements: true, // allow customized built-ins }, } ); // <foo-bar baz="foobar"></foo-bar><div is="foo-baz"></div> /** * Control behavior relating to URI values */ // extend the existing array of elements that can use Data URIs var clean = DOMPurify.sanitize(dirty, {ADD_DATA_URI_TAGS: ['a', 'area']}); // extend the existing array of elements that are safe for URI-like values (be careful, XSS risk) var clean = DOMPurify.sanitize(dirty, {ADD_URI_SAFE_ATTR: ['my-attr']}); /** * Control permitted attribute values */ // allow external protocol handlers in URL attributes (default is false, be careful, XSS risk) // by default only http, https, ftp, ftps, tel, mailto, callto, cid and xmpp are allowed. var clean = DOMPurify.sanitize(dirty, {ALLOW_UNKNOWN_PROTOCOLS: true}); // allow specific protocols handlers in URL attributes via regex (default is false, be careful, XSS risk) // by default only http, https, ftp, ftps, tel, mailto, callto, cid and xmpp are allowed. // Default RegExp: /^(?:(?:(?:f|ht)tps?|mailto|tel|callto|cid|xmpp):|[^a-z]|[a-z+.\-]+(?:[^a-z+.\-:]|$))/i; var clean = DOMPurify.sanitize(dirty, {ALLOWED_URI_REGEXP: /^(?:(?:(?:f|ht)tps?|mailto|tel|callto|cid|xmpp|xxx):|[^a-z]|[a-z+.\-]+(?:[^a-z+.\-:]|$))/i;}); /** * Influence the return-type */ // return a DOM HTMLBodyElement instead of an HTML string (default is false) var clean = DOMPurify.sanitize(dirty, {RETURN_DOM: true}); // return a DOM DocumentFragment instead of an HTML string (default is false) var clean = DOMPurify.sanitize(dirty, {RETURN_DOM_FRAGMENT: true}); // use the RETURN_TRUSTED_TYPE flag to turn on Trusted Types support if available var clean = DOMPurify.sanitize(dirty, {RETURN_TRUSTED_TYPE: true}); // will return a TrustedHTML object instead of a string if possible /** * Influence how we sanitize */ // return entire document including <html> tags (default is false) var clean = DOMPurify.sanitize(dirty, {WHOLE_DOCUMENT: true}); // disable DOM Clobbering protection on output (default is true, handle with care, minor XSS risks here) var clean = DOMPurify.sanitize(dirty, {SANITIZE_DOM: false}); // enforce strict DOM Clobbering protection via namespace isolation (default is false) // when enabled, isolates the namespace of named properties (i.e., `id` and `name` attributes) // from JS variables by prefixing them with the string `user-content-` var clean = DOMPurify.sanitize(dirty, {SANITIZE_NAMED_PROPS: true}); // keep an element's content when the element is removed (default is true) var clean = DOMPurify.sanitize(dirty, {KEEP_CONTENT: false}); // glue elements like style, script or others to document.body and prevent unintuitive browser behavior in several edge-cases (default is false) var clean = DOMPurify.sanitize(dirty, {FORCE_BODY: true}); // remove all <a> elements under <p> elements that are removed var clean = DOMPurify.sanitize(dirty, {FORBID_CONTENTS: ['a'], FORBID_TAGS: ['p']}); // change the parser type so sanitized data is treated as XML and not as HTML, which is the default var clean = DOMPurify.sanitize(dirty, {PARSER_MEDIA_TYPE: 'application/xhtml+xml'}); /** * Influence where we sanitize */ // use the IN_PLACE mode to sanitize a node "in place", which is much faster depending on how you use DOMPurify var dirty = document.createElement('a'); dirty.setAttribute('href', 'javascript:alert(1)'); var clean = DOMPurify.sanitize(dirty, {IN_PLACE: true}); // see https://github.com/cure53/DOMPurify/issues/288 for more info
这里还有更多示例,展示了如何运行、自定义和配置 DOMPurify 以满足您的需求。
持久配置
您可以使用 DOMPurify.setConfig
方法,而不是反复将相同的配置传递给 DOMPurify.sanitize
。您的配置将一直持续到您下一次调用 DOMPurify.setConfig
,或者您调用 DOMPurify.clearConfig
来重置它。请记住,只有一个活动配置,这意味着一旦设置,传递给 DOMPurify.sanitize
的所有额外配置参数都将被忽略。
钩子
DOMPurify 允许您通过使用 DOMPurify.addHook
方法将一个或多个函数附加到以下钩子之一来扩展其功能
beforeSanitizeElements
uponSanitizeElement
(没有 's' - 对每个元素调用)afterSanitizeElements
beforeSanitizeAttributes
uponSanitizeAttribute
afterSanitizeAttributes
beforeSanitizeShadowDOM
uponSanitizeShadowNode
afterSanitizeShadowDOM
当需要时,它将当前处理的 DOM 节点、带有验证节点和属性数据的文本和 DOMPurify 配置传递给回调。查看 MentalJS 钩子演示,了解如何优雅地使用 API。
示例:
DOMPurify.addHook( 'beforeSanitizeElements', function (currentNode, hookEvent, config) { // Do something with the current node and return it // You can also mutate hookEvent (i.e. set hookEvent.forceKeepAttr = true) return currentNode; } );
持续集成
我们目前正在使用 Github Actions 与 BrowserStack 结合使用。这使我们能够在所有支持的浏览器中确认每个提交都按照计划进行。查看构建日志:https://github.com/cure53/DOMPurify/actions
您还可以通过执行 npm test
来运行本地测试。这些测试与 Node.js v0.6.2 和 jsdom@8.5.0 一起工作得很好。
所有相关的提交都将使用 0x24BB6BF4
密钥签名,以提供额外的安全性(自 2016 年 4 月 8 日起)。
开发和贡献
安装(《npm i》)
我们官方支持 npm
。GitHub Actions 工作流程配置为使用 npm
安装依赖项。当使用过时的 npm
版本时,我们无法完全确保已安装依赖项的版本,这可能导致不可预见的问题。
脚本
我们依赖于 npm run-scripts 来与我们的工具基础设施集成。我们使用 ESLint 作为预提交钩子以确保代码一致性。此外,为了简化格式化,我们在构建 /dist
资产时使用 prettier,而构建资产是通过 rollup
进行的。
以下是我们的一些 npm 脚本
npm run dev
以在监视源更改的同时启动构建npm run test
通过 jsdom 和 karma 运行我们的测试套件test:jsdom
仅通过 jsdom 运行测试test:karma
仅通过 karma 运行测试
npm run lint
使用 ESLint(通过 xo)对源进行审核npm run format
使用 prettier 格式化我们的源,以简化通过 ESLintnpm run build
构建我们的分发包,以作为精简和未精简的 UMD 模块npm run build:umd
仅构建未精简的 UMD 模块npm run build:umd:min
仅构建精简的 UMD 模块
注意:所有通过 npm run <script>
触发的运行脚本。
npm 脚本更多,但它们主要是为了与持续集成(CI)集成,或者是“私有”的,例如,每次提交时都要修改构建分发文件。
安全邮件列表
我们维护一个邮件列表,每当发布 DOMPurify 的安全关键版本时,都会通知列表。这意味着,如果有人发现了一个绕过方法,并且我们通过一个版本修复了它(这在发现绕过方法时总是会发生),那么就会向该列表发送邮件。这通常发生在几分钟或几小时后得知绕过方法。您可以通过以下链接订阅此列表
https://lists.ruhr-uni-bochum.de/mailman/listinfo/dompurify-security
特性版本发布将不会在此列表中宣布。
谁做出了贡献?
许多人帮助并正在帮助 DOMPurify 变成它现在所是的样子,需要在此处给予认可!
JGraph 💸, GitHub 💸, CynegeticIO 💸, Sentry 💸, jarrodldavis 💸, GrantGryczan, Lowdefy 💸, granlem , oreoshake , dcramer 💸,tdeekens ❤️, peernohell ❤️, is2ei, SoheilKhodayari, franktopel, NateScarlet, neilj, fhemberger, Joris-van-der-Wel, ydaniv, terjanq, filedescriptor, ConradIrwin, gibson042, choumx, 0xSobky, styfle, koto, tlau88, strugee, oparoz, mathiasbynens, edg2s, dnkolegov, dhardtke, wirehead, thorn0, styu, mozfreddyb, mikesamuel, jorangreef, jimmyhchan, jameydeorio, jameskraus, hyderali, hansottowirtz, hackvertor, freddyb, flavorjones, djfarrelly, devd, camerondunford, buu700, buildog, alabiaga, Vector919, Robbert, GreLI, FuzzySockets, ArtemBernatskyy, @garethheyes, @shafigullin, @mmrupp, @irsdl,ShikariSenpai, ansjdnakjdnajkd, @asutherland, @mathias, @cgvwzq, @robbertatwork, @giutro, @CmdEngineer_, @avr4mit and especially @securitymb ❤️ & @masatokinugawa ❤️
测试由以下提供支持
最后但同样重要的是,感谢BrowserStack 开源计划为我们免费提供他们的服务,并在此之上提供卓越、专业和非常专业的支持。