JavaScript加载局部视图被HP Fortify检出潜在XSS漏洞修复咨询
漏洞触发原因
HP Fortify 报XSS漏洞的核心命中逻辑是:代码中通过AJAX获取返回内容后,未做任何安全校验过滤,就直接通过jQuery的.html()方法插入DOM。哪怕接口是本地服务,只要返回的HTML中混入了未转义的用户可控恶意脚本、on*事件属性、javascript:伪协议链接,插入页面后就会自动执行,触发XSS风险。
另外原代码catch块还存在语法问题:选择器前缺少jQuery对象包裹,会直接抛JS错误。
适配高频调用场景的修复方案
因为该函数是全局高频调用的公共方法,修复优先保证调用方零改动、兼容原有逻辑、安全规则达标,按改造成本从低到高可选以下方案:
方案1:公共函数内嵌清洗逻辑(改造成本最低,快速落地)
只需要修改函数内部逻辑,所有现有调用点完全不需要调整,即可同时满足安全扫描要求和实际XSS防护需求:
- 先对传入的
div参数做格式校验,仅允许由字母、数字、下划线、中划线组成的合法DOM ID,阻断选择器注入风险。 - 内置轻量HTML清洗逻辑,在内容插入DOM前,移除所有危险标签、事件属性、恶意协议,从DOM注入端阻断XSS执行。
- 修复原catch块的语法bug。
修复后的完整代码如下:
// 内置无依赖HTML安全清洗方法 function sanitizeHtml(dirtyHtml) { const tempDom = document.createElement('div'); tempDom.innerHTML = dirtyHtml; // 移除高风险标签 const riskTags = ['script', 'iframe', 'object', 'embed', 'frame', 'link']; riskTags.forEach(tagName => { tempDom.querySelectorAll(tagName).forEach(el => el.remove()); }); // 遍历所有元素,移除风险属性 const allNodes = tempDom.querySelectorAll('*'); const eventAttrReg = /^on/i; const riskProtocolReg = /^(javascript|vbscript|data):/i; allNodes.forEach(node => { Array.from(node.attributes).forEach(attr => { // 移除所有on开头的事件属性 if (eventAttrReg.test(attr.name)) { node.removeAttribute(attr.name); } // 移除href/src/action属性上的危险协议 if (['href', 'src', 'action'].includes(attr.name.toLowerCase()) && riskProtocolReg.test(attr.value.trim())) { node.removeAttribute(attr.name); } }); }); return tempDom.innerHTML; } function loadPartialViewToDiv(div, obj, api) { try { // 校验容器ID合法性,阻断选择器注入 if (!/^[a-zA-Z0-9_-]+$/.test(div)) throw new Error('Invalid container id'); const myUrl = new URL(window.location.origin + api); $.ajax({ url: myUrl, data: obj, cache: false, type: "POST", dataType: "html", success: function (data) { if (data != null && data != undefined) { // 清洗后再插入DOM $('#' + div).html(sanitizeHtml(data)); } } }); } catch (e) { // 修复原代码缺少jQuery选择器的bug if (/^[a-zA-Z0-9_-]+$/.test(div)) { $('#' + div).html('Error'); } } }
这个方案的优势非常明显:所有改动都封装在公共函数内部,业务侧零改造成本,内置的清洗逻辑没有额外第三方依赖,能覆盖绝大多数常规XSS攻击场景,同时因为在DOM注入点前明确增加了输入过滤逻辑,完全可以通过Fortify的静态规则检测,不会再报XSS风险。
方案2:服务端+客户端纵深防护(长期维护推荐)
因为所有接口都是本地局部视图,从源头做防护的安全性更高:
- 服务端渲染局部视图时,对所有用户可控的输出内容默认做HTML实体编码,从接口返回层就避免恶意脚本混入返回内容。
- 对需要返回富文本内容的接口,服务端提前做标签白名单过滤,仅允许保留无执行风险的结构标签、样式属性,禁止返回脚本、事件属性、危险标签。
- 接口增加CSRF校验,避免跨站伪造请求诱导接口返回恶意内容。
方案3:可信场景白名单适配
如果部分调用场景确实需要渲染带合法交互逻辑、脚本的可信局部视图,不要直接全局关闭清洗逻辑,可以给函数增加可选的可信标记参数:
- 新增默认值为
false的可选参数isTrusted,默认走清洗逻辑; - 只有经过安全确认,接口返回内容完全由服务端控制、无任何用户输入拼接的场景,调用时才传入
true跳过清洗,这类调用需要单独加注释标注,避免被滥用。
参数调整参考:
function loadPartialViewToDiv(div, obj, api, isTrusted = false) { // 省略其他重复逻辑 success: function (data) { if (data != null && data != undefined) { const renderContent = isTrusted ? data : sanitizeHtml(data); $('#' + div).html(renderContent); } } }
注意:不要为了过扫描直接把AJAX返回内容标记为可信、或者关闭Fortify对应检测规则,这种操作没有解决实际风险,真实攻击场景下依然会被XSS打穿。
内容的提问来源于stack exchange,提问作者suvsuv
相关产品推荐
相关产品推荐

