前端安全MarkdownXSS性能优化AI 产品
Markdown 渲染陷阱:代码块、表格与 XSS(AI 内容展示必修课)
AI 产品里 Markdown 渲染不是“加个库就完事”,它同时是安全边界与性能瓶颈:XSS、链接钓鱼、图片追踪、超长代码块卡死、表格移动端崩坏。本文给出一套可落地的渲染流水线:分离解析与展示、默认不信任、白名单 + sanitize、代码块与表格性能策略,并提供测试用例清单。
2026年3月4日
Synthly 团队
预计阅读 15 分钟

📷 Photo by Negative Space via Pexels
把 Markdown 当“富文本输入”看待,而不是“展示格式”
很多团队把 Markdown 渲染当成 UI 小事,最后往往被两类问题打爆:
- 安全事故:XSS、链接钓鱼、隐私追踪
- 体验事故:卡顿、滚动跳动、移动端表格炸裂
在 AI 产品里,这些风险被放大:
- 输出更长、更频繁(流式)
- 内容更不可控(模型与用户输入都可能含恶意)
所以正确姿势是:把 Markdown 渲染当成一条“安全与性能流水线”。
一、威胁模型:你到底在防什么?
建议先把风险分层,避免只修表面。
1)XSS:脚本执行与 DOM 注入
典型 payload:
<img src=x onerror=alert(1)>
[click](<javascript:alert(1)>)
<svg><script>alert(1)</script></svg>
你需要防的是:
- 事件属性(onerror/onload)
javascript:/data:协议- SVG/MathML 的复杂注入面
- 通过 HTML 标签、属性、URL 的组合绕过
2)钓鱼与劫持:链接与 opener
- 伪装链接文字
- 通过
target=_blank+ 没有noopener劫持 - 跳转到相似域名
3)隐私与追踪:外链图片、像素、外部资源
<img src="https://tracker.com/pixel?...">- 自动加载外链资源泄露 IP/UA
4)性能与稳定性:长文本、长代码块、巨大表格
- 10 万字符代码块导致高亮卡死
- 200 列表格导致布局崩溃
- 流式增量渲染反复重排
二、正确的渲染流水线:解析、清洗、渲染分离
1)原则:默认不信任
- AI 输出不可信
- 用户输入不可信
- 第三方 Markdown 库不等于安全
2)推荐流水线
- Parse(解析):Markdown → AST/HTML
- Sanitize(清洗):白名单过滤标签/属性/协议
- Render(渲染):安全 HTML → DOM/组件
- Enhance(增强):代码高亮、表格滚动、复制按钮(可选)
关键点:Sanitize 必须是统一入口,不要让不同组件各做一套。
三、白名单策略:你允许什么,就只允许什么
不要尝试“屏蔽坏的”,要尝试“只放行好的”。
1)允许的标签建议(示例)
- 文本:
p,br,strong,em,code,pre,blockquote - 列表:
ul,ol,li - 标题:
h1~h3(更深层级通常不需要) - 链接:
a - 表格:
table,thead,tbody,tr,th,td
2)链接协议白名单
- ✅
http:https:mailto:(按需) - ❌
javascript:data:file:
3)属性白名单
a:href,titlecode/pre:class(用于语言标记,但要防止 class 注入影响样式)
任何 on* 事件属性一律禁止。
四、代码块:性能与安全要一起管
1)最大长度保护(硬阈值)
建议设置:
- 单代码块最大字符数(例如 20k)
- 超过阈值:不做高亮,只做纯文本 + 折叠
2)延迟高亮(Idle/交互后)
高亮通常最耗时。
策略:
- 首次渲染只展示纯文本
- 浏览器空闲(
requestIdleCallback)再做高亮 - 或者用户展开/滚动到可视区域再高亮
3)流式输出下的增量策略
流式时每个 token 都触发重新高亮,会直接卡死。
可行做法:
- 只在“块完成”(例如收到
done或段落边界事件)后高亮 - 或对代码块做缓冲:每 200ms 批量更新一次
五、表格:移动端可读性与布局稳定
表格是 Markdown 渲染中最容易炸的组件。
建议策略:
- 外层包一层可横向滚动容器(
overflow-x: auto) - 限制单元格最大宽度,超出省略 + 点击展开(如有需求)
- 对超大表格降级为 CSV 下载链接(视产品需求)
重点是:避免表格撑爆布局导致页面左右横滑。
六、链接与图片:安全默认值
1)链接安全默认值
- 强制
target="_blank" - 强制
rel="noopener noreferrer" - 可选:对外链显示域名提示
2)图片策略(建议默认保守)
- 不允许任意外链图片(或代理转发)
- 至少要做域名白名单
- 对
data:图片谨慎(可能很大,也可能藏 payload)
七、测试用例清单(建议写成自动化)
把下面这份当作回归集:
1)XSS 与协议绕过
-
<img src=x onerror=alert(1)>不执行 -
[x](javascript:alert(1))被移除或变成纯文本 -
<svg><script>...</script></svg>被移除
2)链接安全
- 外链都有
rel="noopener noreferrer" - 不允许
data:/javascript:
3)性能
- 2 万字符代码块不会卡死(降级策略生效)
- 流式输出不会导致滚动疯狂跳动
4)布局
- 50 列表格移动端不撑爆布局(可横滑)
八、上线 Checklist(把渲染当成安全组件)
- 统一入口:所有 Markdown 都经过同一个 sanitize
- 白名单:标签/属性/协议严格放行
- 链接默认值:noopener/noreferrer
- 代码块阈值:长度保护 + 延迟高亮
- 表格降级:横滑容器 + 宽度限制
- 回归集:XSS/性能/布局用例可自动化
常见问题
我用了成熟的 Markdown 库,还需要 sanitize 吗?
需要。Markdown 库的目标是“解析正确”,不是“安全正确”。安全需要由你定义白名单并在统一入口强制执行。
sanitize 会不会破坏格式?
会,但这是设计结果:你应该明确“支持哪些格式”。对 AI 产品来说,稳定与安全比支持所有 HTML 更重要。