bluemonday默认禁止标签以防xss,需显式允许src属性并限制协议和域名;markdown解析后需放行等基础标签及href、id、class等安全属性;策略配置错误会导致静默返回空字符串,应通过tohtml获取错误信息定位问题。

Bluemonday默认不放行<img alt="如何在Golang中处理Markdown转HTML的安全过滤 Go语言Bluemonday库防XSS" >标签,但业务需要显示图片怎么办
Bluemonday的uglyPolicy和relaxedPolicy都默认禁止<img alt="如何在Golang中处理Markdown转HTML的安全过滤 Go语言Bluemonday库防XSS" >,不是忘了配,是设计如此——XSS高危入口。真要支持图片,得显式允许src属性,且必须限制协议和域名。
实操建议:
立即学习“go语言免费学习笔记(深入)”;
- 用
p.AllowAttrs("src").Matching(httpsRegex).OnElements("img"),其中httpsRegex推荐regexp.MustCompile(`^https?:\/\/[^\s]+$`) - 若需内网图或CDN,把
https?换成更严格的域名白名单,比如^https?:\/\/(i\.example\.com|cdn\.example\.net)\/.*$ - 绝对不要用
AllowAllAttributes()或AllowAllElements(),这等于关掉过滤 -
alt、width、height可酌情放开,但onerror、onclick这类事件属性永远禁止
用blackfriday或goldmark转Markdown时,HTML输出被Bluemonday二次过滤导致样式丢失
Markdown解析器(如goldmark)默认会把**bold**转成<strong></strong>,而Bluemonday的relaxedPolicy虽允许<strong></strong>,但若你用了自定义策略又没显式放行,就会被干掉——不是转换失败,是过滤太狠。
实操建议:
立即学习“go语言免费学习笔记(深入)”;
- 确认你的
bluemonday.Policy调用了.AllowElements("strong", "em", "p", "br", "ul", "ol", "li", "a")等基础语义标签 -
<a></a>必须配AllowAttrs("href").Matching(safeURLRegex),否则所有链接变纯文本 - 如果用了
goldmark.WithExtensions(goldmark.ExtHeadingIDs),生成的id属性默认被删,需加.AllowAttrs("id").Globally() - 避免在Markdown里写
<div class="note">...——Bluemonday默认禁<code><div>,强行开会导致不可控<h3> <code>bluemonday.Sanitize()返回空字符串却不报错,怎么定位是哪步拦住了内容这是最常踩的坑:输入一段带
<script></script>或onmouseover=的Markdown,Sanitize()静默返回空串,日志也没提示。原因不是崩溃,是Bluemonday内部把非法节点全删光后,只剩空DOM树,最终输出空字符串。实操建议:
立即学习“go语言免费学习笔记(深入)”;
- 先用
policy.ToHTML([]byte(markdownHTML), nil)代替Sanitize(),它返回[]byte和error,错误里会含“unrecognized element”或“disallowed attribute”线索 - 临时把策略设为
bluemonday.UGLY_POLICY跑一次,看是否输出恢复——如果恢复,说明你自定义策略漏配了关键元素 - 对输入做预检:
strings.Contains(input, "<script strings.contains>,提前拦截并记录,比事后查空更高效</script> - 别依赖
len(output) == 0判断失败,要检查output是否全空白(strings.TrimSpace后再判)
Bluemonday + Goldmark组合下,
class属性被过滤导致代码块无语法高亮Goldmark默认给
<pre class="brush:php;toolbar:false;"><code class="go"></code>加<code>class</code>,但Bluemonday默认不放行<code>class</code>——所以高亮JS找不到目标元素,不是JS没加载,是DOM里压根没<code>class</code>。</p><p>实操建议:</p><p><span>立即学习</span>“<a href="https://pan.quark.cn/s/00968c3c2c15" style="text-decoration: underline !important; color: blue; font-weight: bolder;" rel="nofollow" target="_blank">go语言免费学习笔记(深入)</a>”;</p><ul><li>加<code>.AllowAttrs("class").OnElements("code", "pre")</code>,别只写<code>"code"</code>,<code><pre class="brush:php;toolbar:false;"></code>也需要(有些主题靠它控制边距)</li><li>若用Prism.js,它还读<code>data-language</code>,得补上<code>.AllowAttrs("data-language").OnElements("code")</code></li><li>禁止用<code>Globally()</code>放开<code>class</code>,否则<code>@@##@@</code>也能过</li><li>测试时用<pre class="brush:php;toolbar:false;">echo '<pre class='brush:go;toolbar:false;'> fmt.Println("hi")</pre>' | goldmark -f - | bluemonday-sanitize直连管道,比埋进Web handler里更容易复现<p>Bluemonday的策略对象不是一次配置终身有效——每次修改都要重新调用<code>Allow*方法生成新实例;缓存一个策略变量反复用没问题,但千万别在HTTP handler里每次新建策略再调AllowElements,那等于每次都在重定义,性能差还容易配错。 - 先用











