Loading... > bluemonday:一种快速的 golang HTML 清理器(受 OWASP Java HTML 清理器的启发),用于清除用户生成的 XSS 内容 bluemonday 是一个用 Go 实现的 HTML 清理器。它速度快且高度可配置。 bluemonday 将不受信任的用户生成的内容作为输入,并将返回已根据批准的 HTML 元素和属性的白名单进行清理的 HTML,以便您可以安全地将内容包含在您的网页中。 如果你接受用户生成的内容,并且你的服务器使用 Go,你**需要**bluemonday。 用户生成的内容 ( `bluemonday.UGCPolicy().Sanitize()`) 的默认策略是这样的: ```html Hello <STYLE>.XSS{background-image:url("javascript:alert('XSS')");}</STYLE><A CLASS=XSS></A>World ``` 变成无害的: ```html Hello World ``` 它变成了这个: ```html <a href="javascript:alert('XSS1')" onmouseover="alert('XSS2')">XSS<a> ``` 进入这个: ```html XSS ``` 虽然仍然允许这样做: ```html <a href="http://www.google.com/"> <img src="https://ssl.gstatic.com/accounts/ui/logo_2x.png"/> </a> ``` 基本保持不变(它获得了一个 rel="nofollow",这对用户生成的内容来说是一件好事): ```html <a href="http://www.google.com/" rel="nofollow"> <img src="https://ssl.gstatic.com/accounts/ui/logo_2x.png"/> </a> ``` 它保护站点免受[XSS](http://en.wikipedia.org/wiki/Cross-site_scripting)攻击。[XSS 攻击](https://www.owasp.org/index.php/XSS_Filter_Evasion_Cheat_Sheet)有很多载体,降低风险的最佳方法是根据已知的 HTML 元素和属性安全列表清理用户输入。 您应该**始终**在任何其他处理**之后**运行 bluemonday 。 如果您使用[blackfriday](https://github.com/russross/blackfriday)或[Pandoc](http://johnmacfarlane.net/pandoc/),那么应该在这些步骤之后运行 bluemonday。这可确保稍后在您的流程中不会引入不安全的 HTML。 bluemonday 深受[OWASP Java HTML Sanitizer](https://code.google.com/p/owasp-java-html-sanitizer/)和[HTML Purifier](http://htmlpurifier.org/)的启发。 ## 技术概要 基于白名单,您需要构建一个描述要允许的 HTML 元素和属性(以及`regexp`属性模式)的策略,或者使用提供的表示良好默认值的策略之一。 包含白名单的策略是使用由核心 Go 团队在[Go net/html 库](https://godoc.org/golang.org/x/net/html)中实现的快速非验证、仅转发、基于令牌的解析器来应用的。 我们希望提供格式良好的 HTML(每个适用的开放元素的闭合元素,正确嵌套),因此我们不会专注于修复嵌套错误或不完整的 HTML。我们只专注于确保政策允许列表中描述了任何确实存在的元素,并且属性和链接可以安全地用于您的网页。[GIGO](http://en.wikipedia.org/wiki/Garbage_in,_garbage_out)确实适用,如果你给它提供糟糕的 HTML,bluemonday 不会负责弄清楚如何让它再次变好。 ### 支持的版本 bluemonday 在 Go 1.2 之后的所有版本上进行了测试,包括 tip。 我们不支持 Go 1.0,因为我们依赖于`golang.org/x/net/html`其中包含对`io.ErrNoProgress`Go 1.0 中不存在的引用。 我们支持 Go 1.1,但 Travis 不再针对它进行测试。 ## 生产就绪了吗? *是的* 我们在生产中使用 bluemonday,它是从广泛使用且经过大量现场测试的 OWASP Java HTML Sanitizer 迁移过来的。 我们正在通过我们广泛的测试套件(包括 AntiSamy 测试以及针对所提出的任何问题的测试)。检查是否有任何[未解决的问题](https://github.com/microcosm-cc/bluemonday/issues?page=1&state=open),看看是否有任何问题可能成为您的障碍。 我们邀请拉取请求和问题来帮助我们确保我们通过用户生成的内容提供针对各种攻击的全面保护。 ## 用法 在您的`${GOPATH}`使用中安装`go get -u github.com/microcosm-cc/bluemonday` 然后调用它: ```go notranslate position-relative overflow-auto package main import ( "fmt" "github.com/microcosm-cc/bluemonday" ) func main() { // Do this once for each unique policy, and use the policy for the life of the program // Policy creation/editing is not safe to use in multiple goroutines p := bluemonday.UGCPolicy() // The policy can then be used to sanitize lots of input and it is safe to use the policy in multiple goroutines html := p.Sanitize( `<a onblur="alert(secret)" href="http://www.google.com">Google</a>`, ) // Output: // <a href="http://www.google.com" rel="nofollow">Google</a> fmt.Println(html) } ``` 我们提供三种调用 Sanitize 的方法: ```go notranslate position-relative overflow-auto p.Sanitize(string) string p.SanitizeBytes([]byte) []byte p.SanitizeReader(io.Reader) bytes.Buffer ``` 如果您痴迷于性能,`p.SanitizeReader(r).Bytes()`将返回 a`[]byte`而不执行任何不必要的输入或输出转换。尽管差异可以忽略不计,但您永远不需要关心。 您可以建立自己的政策: ```go notranslate position-relative overflow-auto package main import ( "fmt" "github.com/microcosm-cc/bluemonday" ) func main() { p := bluemonday.NewPolicy() // Require URLs to be parseable by net/url.Parse and either: // mailto: http:// or https:// p.AllowStandardURLs() // We only allow <p> and <a href=""> p.AllowAttrs("href").OnElements("a") p.AllowElements("p") html := p.Sanitize( `<a onblur="alert(secret)" href="http://www.google.com">Google</a>`, ) // Output: // <span class="external-link"><a class="no-external-link" href="http://www.google.com" target="_blank"><i data-feather="external-link"></i>Google</a></span> fmt.Println(html) } ``` 我们发布了两个默认策略: 1. `bluemonday.StrictPolicy()`这可以被认为等同于剥离所有 HTML 元素及其属性,因为它的白名单上没有任何内容。一个示例使用场景是博客文章标题,其中根本不需要 HTML 标记,如果它们是元素 *,* 则应删除元素的内容。这是一项*非常*严格的政策。 2. `bluemonday.UGCPolicy()`它允许广泛选择对用户生成的内容是安全的 HTML 元素和属性。请注意,此策略*不允许*iframe、对象、嵌入、样式、脚本等。示例使用场景是博客文章正文,其中预期会出现各种格式以及可能的 TABLE 和 IMG。 ## 政策建设 构建策略的本质是确定哪些 HTML 元素和属性被认为对您的场景是安全的。OWASP 提供了一个[XSS 预防备忘单](https://www.owasp.org/index.php/XSS_(Cross_Site_Scripting)_Prevention_Cheat_Sheet)来帮助解释风险,但本质上: 1. 避免使用标准 HTML 元素以外的任何元素 2. 避免`script`, `style`, `iframe`, `object`, `embed`,`base`允许客户端执行代码的元素或包含可以执行代码的第三方内容 3. 避免使用值与正则表达式匹配的纯 HTML 属性以外的任何内容 基本上,您应该能够描述哪种 HTML 适合您的场景。如果您没有信心可以描述您的保单,请考虑使用其中一项已发货的保单,例如`bluemonday.UGCPolicy()`。 要创建新策略: ```go notranslate position-relative overflow-auto p := bluemonday.NewPolicy() ``` 要向策略添加元素,请只添加元素: ```go notranslate position-relative overflow-auto p.AllowElements("b", "strong") ``` 或者使用正则表达式: *注意:如果如上所示按名称添加元素,则将忽略任何匹配的正则表达式* 还建议确保多个模式不重叠,因为无法保证执行顺序并且可能导致某些规则被遗漏。 ```go notranslate position-relative overflow-auto p.AllowElementsMatching(regex.MustCompile(`^my-element-`)) ``` 或者添加元素作为添加属性的优点: ```go notranslate position-relative overflow-auto // Note the recommended pattern, see the recommendation on using .Matching() below p.AllowAttrs("nowrap").OnElements("td", "th") ``` 同样,这也支持正则表达式模式匹配替代方案: ```go notranslate position-relative overflow-auto p.AllowAttrs("nowrap").OnElementsMatching(regex.MustCompile(`^my-element-`)) ``` 可以将属性添加到所有元素: ```go notranslate position-relative overflow-auto p.AllowAttrs("dir").Matching(regexp.MustCompile("(?i)rtl|ltr")).Globally() ``` 或者可以将属性添加到特定元素: ```go notranslate position-relative overflow-auto // Not the recommended pattern, see the recommendation on using .Matching() below p.AllowAttrs("value").OnElements("li") ``` 始终建议使属性与模式相匹配 **。** 否则 HTML 属性中的 XSS 非常容易: ```go notranslate position-relative overflow-auto // \p{L} matches unicode letters, \p{N} matches unicode numbers p.AllowAttrs("title").Matching(regexp.MustCompile(`[\p{L}\p{N}\s\-_',:\[\]!\./\\\(\)&]*`)).Globally() ``` 您可以随时停止并调用 .Sanitize(): ```go notranslate position-relative overflow-auto // string htmlIn passed in from a HTTP POST htmlOut := p.Sanitize(htmlIn) ``` 您可以采用任何现有政策并对其进行扩展: ```go notranslate position-relative overflow-auto p := bluemonday.UGCPolicy() p.AllowElements("fieldset", "select", "option") ``` ### 内联 CSS 尽管可以使用规则来处理内联 CSS `AllowAttrs`,`Matching`但编写一个单一的正则表达式来安全地处理您希望允许的所有内联 CSS 并不是一项简单的任务。`style`您可以在您想要的任何元素上允许该属性,并使用样式策略来控制和清理内联样式,而不是尝试这样做。 强烈建议您使用`Matching`(with a suitable regular expression) `MatchingEnum`或`MatchingHandler`以确保每种样式都满足您的需求,但默认处理程序是为最广泛使用的样式提供的。 与属性类似,您可以允许内联设置特定的 CSS 属性: ```go notranslate position-relative overflow-auto p.AllowAttrs("style").OnElements("span", "p") // Allow the 'color' property with valid RGB(A) hex values only (on any element allowed a 'style' attribute) p.AllowStyles("color").Matching(regexp.MustCompile("(?i)^#([0-9a-f]{3,4}|[0-9a-f]{6}|[0-9a-f]{8})$")).Globally() ``` 此外,您可以只允许将 CSS 属性设置为允许的值: ```go notranslate position-relative overflow-auto p.AllowAttrs("style").OnElements("span", "p") // Allow the 'text-decoration' property to be set to 'underline', 'line-through' or 'none' // on 'span' elements only p.AllowStyles("text-decoration").MatchingEnum("underline", "line-through", "none").OnElements("span") ``` 或者您可以根据正则表达式模式匹配指定元素: ```go notranslate position-relative overflow-auto p.AllowAttrs("style").OnElementsMatching(regex.MustCompile(`^my-element-`)) // Allow the 'text-decoration' property to be set to 'underline', 'line-through' or 'none' // on 'span' elements only p.AllowStyles("text-decoration").MatchingEnum("underline", "line-through", "none").OnElementsMatching(regex.MustCompile(`^my-element-`)) ``` 如果您需要更具体的检查,您可以创建一个处理程序,它接受一个字符串并返回一个布尔值来验证给定属性的值。字符串参数已转换为小写,并且已转换 unicode 代码点。 ```go notranslate position-relative overflow-auto myHandler := func(value string) bool{ // Validate your input here return true } p.AllowAttrs("style").OnElements("span", "p") // Allow the 'color' property with values validated by the handler (on any element allowed a 'style' attribute) p.AllowStyles("color").MatchingHandler(myHandler).Globally() ``` ### 链接 链接是难以安全清理的野兽,也是恶意内容的最大攻击媒介之一。 可以这样做: ```go notranslate position-relative overflow-auto p.AllowAttrs("href").Matching(regexp.MustCompile(`(?i)mailto|https?`)).OnElements("a") ``` 但这并不能保护你,因为在这种情况下,正则表达式不足以阻止格式错误的值做一些意想不到的事情。 我们提供了一些额外的全局选项来安全地使用链接。 `RequireParseableURLs`将确保 URL 可以被 Go 的`net/url`包解析: ```go notranslate position-relative overflow-auto p.RequireParseableURLs(true) ``` 如果您启用了可解析的 URL,则以下选项将`AllowRelativeURLs`。默认情况下这是禁用的(bluemonday 是一个白名单工具......你需要明确告诉我们允许事情)并且当禁用它时它将阻止所有本地和方案相对 URL(即`href="localpage.html"`,`href="../home.html"`甚至`href="//www.google.com"`是相对的): ```go notranslate position-relative overflow-auto p.AllowRelativeURLs(true) ``` 如果您启用了可解析的 URL,那么您可以允许允许的方案(在考虑`http`和时通常称为协议`https`)。请记住,在上述选项中允许相对 URL 将允许空白方案: ```go notranslate position-relative overflow-auto p.AllowURLSchemes("mailto", "http", "https") ``` 无论您是否启用了可解析的 URL,您都可以强制所有 URL 具有 rel="nofollow" 属性。如果它不存在,将添加它,但仅当`href`它有效时: ```go notranslate position-relative overflow-auto // This applies to "a" "area" "link" elements that have a "href" attribute p.RequireNoFollowOnLinks(true) ``` 同样,您可以强制所有 URL 在其 rel 属性中包含“noreferrer”。 ```go notranslate position-relative overflow-auto // This applies to "a" "area" "link" elements that have a "href" attribute p.RequireNoReferrerOnLinks(true) ``` 我们提供了一种适用于上述所有内容的便捷方法,但您仍需要允许 URL 规则的可链接元素应用于: ```go notranslate position-relative overflow-auto p.AllowStandardURLs() p.AllowAttrs("cite").OnElements("blockquote", "q") p.AllowAttrs("href").OnElements("a", "area") p.AllowAttrs("src").OnElements("img") ``` 关于链接的额外复杂性是[RFC2397](http://tools.ietf.org/html/rfc2397)中定义的数据 URI 。数据 URI 允许使用以下格式内联提供图像: ```html <img src="data:image/webp;base64,UklGRh4AAABXRUJQVlA4TBEAAAAvAAAAAAfQ//73v/+BiOh/AAA="> ``` 我们提供了一个帮助程序来验证 mimetype 后跟数据 URI 链接的 base64 内容: ```go notranslate position-relative overflow-auto p.AllowDataURIImages() ``` 该助手将启用 GIF、JPEG、PNG 和 WEBP 图像。 应该注意的是,使用数据 URI 链接存在潜在的[安全](http://palizine.plynt.com/issues/2010Oct/bypass-xss-filters/) [风险。](https://capec.mitre.org/data/definitions/244.html)如果您已经信任内容,您应该只启用数据 URI 链接。 我们还有一些功能可以帮助处理用户生成的内容: ```go notranslate position-relative overflow-auto p.AddTargetBlankToFullyQualifiedLinks(true) ``` 这将确保将`<a href="" />`完全限定的锚链接(href 目标包括主机名)`target="_blank"`添加到它们中。 `target="_blank"`此外,应用该策略后的任何链接的`rel`属性也将调整为 add `noopener`。这意味着链接可能`<a href="//host/path"/>`以`<a href="//host/path" rel="noopener" target="_blank">`. 重要的是要注意添加`noopener`是一项安全功能而不是问题。浏览器有一个不幸的特性,即打开的浏览器窗口`target="_blank"`仍然可以控制打开器(您的网页),这可以防止这种情况发生。可以在此处找到相关背景:[https ://dev.to/ben/the-targetblank-vulnerability-by-example](https://dev.to/ben/the-targetblank-vulnerability-by-example) ### 政策制定助手 我们还捆绑了一些助手来简化策略构建: ```go notranslate position-relative overflow-auto // Permits the "dir", "id", "lang", "title" attributes globally p.AllowStandardAttributes() // Permits the "img" element and its standard attributes p.AllowImages() // Permits ordered and unordered lists, and also definition lists p.AllowLists() // Permits HTML tables and all applicable elements and non-styling attributes p.AllowTables() ``` ### 无效指令 以下内容无效: ```go notranslate position-relative overflow-auto // This does not say where the attributes are allowed, you need to add // .Globally() or .OnElements(...) // This will be ignored without error. p.AllowAttrs("value") // This does not say where the attributes are allowed, you need to add // .Globally() or .OnElements(...) // This will be ignored without error. p.AllowAttrs( "type", ).Matching( regexp.MustCompile("(?i)^(circle|disc|square|a|A|i|I|1)$"), ) ``` 这两个示例都表现出相同的问题,它们声明了属性,但没有指定它们是全局允许的还是仅在特定元素(以及哪些元素)上允许。属性属于一个或多个元素,策略需要声明这一点。 ## 限制 我们还没有包括任何工具来帮助允许和清理 CSS。这意味着除非您希望在单个正则表达式中完成繁重的工作(不可取), **否则您不应在任何地方使用“style”属性** 。 在同一主题下,`<script>`和`<style>`都被认为是有害的。默认情况下不会呈现这些元素(及其内容),需要您显式设置`p.AllowUnsafe(true)`. 你应该知道,允许这些元素违背了使用 HTML 消毒器的目的,因为你将明确允许 JavaScript(和任何简单编写的 XSS)和 CSS(可以修改 DOM 以插入 JS),此外还有限制库意味着它不知道 HTML 的结构是否有效,并且可以允许这些元素绕过[WhatWG HTML 解析器标准](https://html.spec.whatwg.org/multipage/parsing.html#parsing-main-inselect)中内置的一些安全机制。 修复不良 HTML 不是 bluemonday 的工作,而只是防止恶意 HTML 通过的 bluemonday 的工作。如果您有不匹配的 HTML 元素,或不符合规范的元素嵌套,这些元素将保留。但是如果你有结构良好的 HTML,bluemonday 就不会破坏它。 ## 去做 * 调查开发人员是否要将元素和属性列入黑名单。这将允许开发人员采用现有策略(例如`bluemonday.UGCPolicy()`)来封装他们正在寻找的 90% 但做的比他们需要的更多,并删除他们不想要的额外东西,使其 100% 成为他们想要的 * 调查开发人员是否需要一种验证 HTML 模式,在这种模式下,HTML 元素不仅被转换为平衡树(每个开始标签在正确的深度都有一个结束标签),而且元素和字符数据仅出现在它们允许的上下文中(即一个`table`元素不是 a 的后代`caption`,那个`colgroup`, `thead`, `tbody`,是允许的,`tfoot`并且`tr`那个字符数据是不允许的) ## 发展 如果你已经克隆了这个 repo,你可能需要依赖项: `go get golang.org/x/net/html` Gophers 可以使用他们熟悉的工具: `go build` `go test` 我个人使用 Makefile,因为它避免了一遍又一遍地输入相同的参数,同时为我们这些从一种语言跳到另一种语言并享受只输入`make`项目目录并观看奇迹发生的人提供一致性。 `make`将构建、审查、测试和安装库。 `make clean`将从*单个* `${GOPATH}/pkg`目录树中删除库 `make test`将运行测试 `make cover`将运行测试并*打开*包含覆盖率报告的浏览器窗口 `make lint`将运行 golint(通过安装`go get github.com/golang/lint/golint`) 该项目 目前2.5k⭐ 项目地址 https://github.com/microcosm-cc/bluemonday 最后修改:2022 年 11 月 17 日 © 允许规范转载 打赏 赞赏作者 微信 赞 3 如果觉得我的文章对你有用,请随意赞赏