去提问»
提问背景 Question:
你有时候发给我,类似下面的代码,我根本看不到,被渲染了。必须通知你重发,这个问题如何解决。比如下面 <script src=xxx/?id=a></script>

解决前端HTML/JS代码被意外渲染的安全方案

发布时间:2026-08-19 17:27 👁️ 阅读:2

这个问题在前后端交互和 AI 问答界面中非常经典。当 AI 返回类似 <script src="..."></script><div> 等原始 HTML 代码时,如果前端直接通过 innerHTML 渲染,浏览器就会把这些代码当作真正的 HTML 标记或 JS 脚本去执行,导致代码消失、页面变形甚至引发 XSS 安全漏洞

要彻底解决这个问题,你需要在前端对数据进行**“防御性”安全处理**。以下是 3 种最有效的改进方法:

方案一:在解析 Markdown 前,全局转义 HTML 敏感字符(最推荐)

在将 AI 返回的 text 送入 marked.parse() 之前,先用正则将所有的 <> 替换为 HTML 实体符号 &lt;&gt;。这样浏览器就会将其作为普通文本显示,而不会去解析和执行它。

修改你的前端 appendMessage 或渲染逻辑,在解析前加入以下转义函数:

// 1. 定义一个高强度的安全转义函数
function escapeRawHtml(str) {
  if (!str) return '';
  // 仅转义可能引起浏览器误解析的裸 HTML 标签,而不破坏已有的 Markdown 标记
  return str.replace(/</g, "&lt;").replace(/>/g, "&gt;");
}

// 2. 在渲染前进行处理
function appendMessage(sender, text, isHtml = false, provider = "") {
  const chatWindow = document.getElementById("chat-window");
  const spacer = document.getElementById("bottom-spacer");
  const msgDiv = document.createElement("div");
  msgDiv.className = "message " + sender;

  if (sender === "ai") {
    // 🌟 核心修正:先转义,再进行 marked 渲染
    const safeText = escapeRawHtml(text);
    const rawHtml = marked.parse(safeText);
    msgDiv.innerHTML = rawHtml;
    
    // ... 后面原有的代码复制逻辑保持不变 ...
  }
  // ...
}

方案二:配置 marked.js 的自定义渲染器(Renderer)

如果你不希望全局转义(担心影响某些合规的 Markdown 渲染),可以通过 marked 提供的 renderer 接口,强行对所有代码块(code)和行内代码(codespan)进行二次转义,确保它们绝对安全。

在你的 <script> 初始化区域,配置 marked 的渲染规则:

const renderer = new marked.Renderer();

// 强行转义代码块内的 HTML 防止执行
renderer.code = function(code, infostring, escaped) {
  const lang = (infostring || '').match(/\S*/)[0];
  // 转义代码内容
  const escapedCode = code
    .replace(/&/g, '&amp;')
    .replace(/</g, '&lt;')
    .replace(/>/g, '&gt;')
    .replace(/"/g, '&quot;')
    .replace(/'/g, '&#39;');

  return `
<div class="code-block-wrapper">
  <pre><code class="language-${lang}">${escapedCode}</code></pre>
</div>`;
};

// 强行转义行内代码
renderer.codespan = function(text) {
  const escapedText = text
    .replace(/&/g, '&amp;')
    .replace(/</g, '&lt;')
    .replace(/>/g, '&gt;');
  return `<code>${escapedText}</code>`;
};

// 将配置应用到 marked
marked.setOptions({
  renderer: renderer,
  breaks: true,
  gfm: true
});

方案三:使用 DOMPurify 过滤库(工业级防 XSS 注入)

如果你的系统未来不仅渲染 AI 返回的内容,还允许用户输入富文本,最标准、最安全的做法是引入 DOM 净化库,例如 DOMPurify。它会自动剔除任何有安全隐患的 <script><iframe>onload 属性,而保留安全的 Markdown 标签。

  1. 引入 CDN
    <script src="https://cdn.jsdelivr.net/npm/dompurify@3.0.5/dist/purify.min.js"></script>
    
  2. 在 innerHTML 渲染时套一层净化
    const rawHtml = marked.parse(text);
    // 🌟 使用 DOMPurify 过滤危险标签后再渲染,彻底防止任何代码被执行
    msgDiv.innerHTML = DOMPurify.sanitize(rawHtml);
    

总结建议

对于你的【百问百答】系统,最直接快捷的办法是方案一(简单替换 <>)。这能立刻解决由于 AI 返回裸标签导致的代码消失和意外渲染问题,而无需引入复杂的第三方库。