提问背景 Question:
你有时候发给我,类似下面的代码,我根本看不到,被渲染了。必须通知你重发,这个问题如何解决。比如下面
<script src=xxx/?id=a></script>
解决前端HTML/JS代码被意外渲染的安全方案
这个问题在前后端交互和 AI 问答界面中非常经典。当 AI 返回类似 <script src="..."></script> 或 <div> 等原始 HTML 代码时,如果前端直接通过 innerHTML 渲染,浏览器就会把这些代码当作真正的 HTML 标记或 JS 脚本去执行,导致代码消失、页面变形甚至引发 XSS 安全漏洞。
要彻底解决这个问题,你需要在前端对数据进行**“防御性”安全处理**。以下是 3 种最有效的改进方法:
方案一:在解析 Markdown 前,全局转义 HTML 敏感字符(最推荐)
在将 AI 返回的 text 送入 marked.parse() 之前,先用正则将所有的 < 和 > 替换为 HTML 实体符号 < 和 >。这样浏览器就会将其作为普通文本显示,而不会去解析和执行它。
修改你的前端 appendMessage 或渲染逻辑,在解析前加入以下转义函数:
// 1. 定义一个高强度的安全转义函数
function escapeRawHtml(str) {
if (!str) return '';
// 仅转义可能引起浏览器误解析的裸 HTML 标签,而不破坏已有的 Markdown 标记
return str.replace(/</g, "<").replace(/>/g, ">");
}
// 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, '&')
.replace(/</g, '<')
.replace(/>/g, '>')
.replace(/"/g, '"')
.replace(/'/g, ''');
return `
<div class="code-block-wrapper">
<pre><code class="language-${lang}">${escapedCode}</code></pre>
</div>`;
};
// 强行转义行内代码
renderer.codespan = function(text) {
const escapedText = text
.replace(/&/g, '&')
.replace(/</g, '<')
.replace(/>/g, '>');
return `<code>${escapedText}</code>`;
};
// 将配置应用到 marked
marked.setOptions({
renderer: renderer,
breaks: true,
gfm: true
});
方案三:使用 DOMPurify 过滤库(工业级防 XSS 注入)
如果你的系统未来不仅渲染 AI 返回的内容,还允许用户输入富文本,最标准、最安全的做法是引入 DOM 净化库,例如 DOMPurify。它会自动剔除任何有安全隐患的 <script>、<iframe>、onload 属性,而保留安全的 Markdown 标签。
- 引入 CDN:
<script src="https://cdn.jsdelivr.net/npm/dompurify@3.0.5/dist/purify.min.js"></script> - 在 innerHTML 渲染时套一层净化:
const rawHtml = marked.parse(text); // 🌟 使用 DOMPurify 过滤危险标签后再渲染,彻底防止任何代码被执行 msgDiv.innerHTML = DOMPurify.sanitize(rawHtml);
总结建议
对于你的【百问百答】系统,最直接快捷的办法是方案一(简单替换 < 和 >)。这能立刻解决由于 AI 返回裸标签导致的代码消失和意外渲染问题,而无需引入复杂的第三方库。