返回文章列表
前端安全MarkdownXSS性能优化AI 产品

Markdown 渲染陷阱:代码块、表格与 XSS(AI 内容展示必修课)

AI 产品里 Markdown 渲染不是“加个库就完事”,它同时是安全边界与性能瓶颈:XSS、链接钓鱼、图片追踪、超长代码块卡死、表格移动端崩坏。本文给出一套可落地的渲染流水线:分离解析与展示、默认不信任、白名单 + sanitize、代码块与表格性能策略,并提供测试用例清单。

2026年3月4日
Synthly 团队
预计阅读 15 分钟
Markdown 渲染与安全:代码块、表格与 XSS 风险点的防护示意图

📷 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)推荐流水线

  1. Parse(解析):Markdown → AST/HTML
  2. Sanitize(清洗):白名单过滤标签/属性/协议
  3. Render(渲染):安全 HTML → DOM/组件
  4. 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, title
  • code/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 更重要。

想看更多工程化文章见 /articles,也可以在 /apps/new 体验 Agent 能力。