HTML 已经转义,为何仍有 XSS?Sharp srcdoc 漏洞中的第二次解析
一、背景与趋势
富文本编辑器需要保留链接、媒体和嵌入内容,安全规则因此从"允许哪些标签"扩展到"允许哪些属性及其语义"。某些属性容纳的是 URL,有些是 CSS,还有些直接容纳完整文档。若统一按文本处理,编码层面的正确性也可能掩盖上下文错误。
已确认事实: Sharp 官方公告于 2026-06-24 披露,GitHub 已审核记录于 2026-09-25收录。本期解读的是新收录记录,不是新发生的攻击。
| 项目 | 已确认内容 |
|---|---|
| 包 | Composer 的 code16/sharp,不是 Node.js 图片库 sharp |
| 标识 | CVE-2026-61823 / GHSA-qxg3-46rw-79j8 |
| 受影响 | <9.22.5 |
| 最低修复 | 9.22.5 |
| 前提 | 可编辑 Editor 字段,并有其他用户查看内容 |
二、技术原理:编码后的属性仍会成为文档
公告确认,富文本处理允许 iframe 的 srcdoc 属性。浏览器在处理属性时会解码实体,再把其中的内容作为子文档解析。于是,对外层属性所做的转义,并未消除内层文档的执行语义。
**工程分析:**安全审计需要追踪数据最终进入什么解释器。字符串第一次输出时是属性值,下一步可能成为 HTML;某一层的安全编码,不能自动继承到下一层。类似原则也适用于模板生成 JavaScript、配置生成命令和数据生成查询,但这些只是工程类比,不是本漏洞的额外攻击链。
官方提交 ec509a2从 iframe 属性白名单移除 srcdoc,并增加对应测试。该提交还处理了其他富文本判断逻辑;本文只围绕 srcdoc 这一项,不把同提交的全部变更都算作这个漏洞的根因。
三、安全实验:不用 JavaScript 也能看懂问题
以下 Python 示例仅处理字符串与字典,不启动浏览器。用普通段落代替任何脚本,观察"属性安全编码"和"内层内容仍是 HTML"可以同时成立。
python
from html import escape, unescape
inner = "<p>仅供演示的文字</p>"
attribute_text = escape(inner, quote=True)
assert "<p>" not in attribute_text
assert unescape(attribute_text) == inner
# 教学用结构化属性筛选,不是生产 HTML 消毒器。
def keep_attributes(attrs):
allowed = {"title", "width", "height"}
return {k: v for k, v in attrs.items() if k in allowed}
result = keep_attributes({"srcdoc": inner, "title": "demo"})
assert "srcdoc" not in result
assert result == {"title": "demo"}
print("nested interpretation checks passed")
生产环境不能用这段字典函数替代成熟消毒器,因为真实 HTML 包含大小写、命名空间、解析修复和多种编码。示例只证明一条设计原则:不需要内嵌文档能力时,删除这个能力入口比继续尝试给它"多转义一次"更容易验证。
四、影响范围与证据边界
存储型 XSS 的危险在于写入与触发分离:低权限编辑者提交内容,另一个身份稍后查看。**工程推断:**若查看者拥有高权限,可能扩大浏览器内可执行操作的影响;但实际可访问数据取决于同源环境、iframe sandbox、Cookie 属性和应用授权。
因此,不能把 XSS 直接等同于"所有管理员 Cookie 必然泄露",更不能写成服务器远程代码执行。HttpOnly 可以限制脚本读取特定 Cookie,却不证明整个页面免受脚本影响。不同防线各自保护不同对象。
五、开发与安全团队如何修复
**立即升级:**核对 composer.lock 与运行环境中的实际包,升级到至少 9.22.5 的兼容修复版本;不要把 npm 的同名包升级当作处置完成。
**治理历史内容:**升级输入消毒流程不一定自动改写数据库里的旧内容。先确定系统在写入、读取还是渲染阶段应用消毒,再在备份上盘点历史富文本。采用解析器进行迁移,验证页面展示与权限流程,避免用正则大范围破坏合法内容。
**建立上下文测试:**对每个允许属性记录其语义和必要性。测试既要断言危险属性已被移除,也要确保正常段落、链接和允许的媒体仍然存在。测试必须覆盖预览页、详情页、管理页和导出路径,而不是只检查编辑器提交接口。
**降低后果:**将低信任富文本与敏感管理操作隔离;按业务需要部署 CSP 和严格的嵌入策略。它们是纵深防护,不能替代库升级和正确消毒。日志只记录内容标识与变更主体,避免复制完整敏感正文。
六、总结
"已经转义"必须补全后半句:针对哪一个解释上下文?Sharp 的案例提醒我们,把富文本属性当作能力集合审查,比笼统地认为"所有输入都已清洗"更可靠。