导读:本期聚焦于卡拉米创作的《jQuery.parseHTML解析脚本标签为什么会执行?详解keepScripts安全过滤选项》,敬请观看详情。为什么用jQuery.parseHTML插入一段包含script标签的HTML字符串,脚本有时会执行有时却被静默丢弃?答案藏在keepScripts这个不起眼的参数里。本文从源码层面拆解parseHTML的完整执行流程,分析jQuery.buildFragment如何借助文档碎片处理字符串,以及正则rquickExpr和标签匹配背后的处理逻辑。同时结合XSS攻击场景,说明默认丢弃脚本的安全设计意图、绕过风险与正确的防御姿势,包括如何安全地动态加载脚本、配合DOMPurify做输入净化,以及在日常开发中规避innerHTML注入漏洞的实践建议。

在前端动态渲染场景中,把一段HTML字符串插入页面几乎是绕不开的操作。不少同学习惯直接用$(selector).html(str)来完成任务,却很少关心这行代码背后发生了什么。jQuery提供了一个更底层的入口jQuery.parseHTML它负责把字符串解析成节点数组,其中对script标签的处理直接关系到页面安全。这篇文章围绕源码把这个过程讲透,重点看keepScripts参数如何决定脚本的生死,以及这个设计在防御XSS上有什么门道。

jQuery.parseHTML解析脚本标签为什么会执行?详解keepScripts安全过滤选项

parseHTML的源码结构与执行流程

先看jQuery 3.x中jQuery.parseHTML的核心实现,函数签名是jQuery.parseHTML(data, context, keepScripts)。三个参数分别代表待解析的HTML字符串、解析所依赖的文档上下文,以及是否保留脚本标签。整个函数的第一步是参数归一化:如果第二个参数是布尔值,说明调用者省略了context,jQuery会自动把参数往后挪一位。

接下来是一段容易被忽略的防御代码:

// 源码节选(简化)
parseHTML: function( data, context, keepScripts ) {
    if ( typeof data !== "string" ) {
        return [];
    }
    if ( typeof context === "boolean" ) {
        keepScripts = context;
        context = false;
    }

    var base, parsed, scripts;

    if ( !context ) {
        // 防止toString导致的意外解析
        if ( !parsedHtmlCache[ data ] ) {
            doc = document.implementation.createHTMLDocument( "" );
            base = doc.createElement( "base" );
            base.href = document.location.href;
            doc.head.appendChild( base );
            parsedHtmlCache[ data ] = doc.body.innerHTML;
        }
    }
    ...
}

这里有个关键细节:当不传context时,jQuery会创建一个脱离主文档的createHTMLDocument沙箱来解析字符串。这个离屏文档不会执行脚本、不加载图片,解析出来的字符串再交给后续流程。这正是防御设计的第一层。最终函数调用jQuery.buildFragment生成文档碎片,并通过jQuery.map(nodes, ...)把碎片中的节点摊平成数组返回。

keepScripts如何决定script标签的命运

buildFragment内部在处理完字符串后,会主动收集所有script标签:scripts = jQuery.map( nodes, function( elem ) { return elem.nodeName.toLowerCase() === "script" ? elem : undefined; } )。随后在返回之前执行这样一段逻辑:

// buildFragment中的关键判断(简化)
if ( !keepScripts && node.parentNode ) {
    jQuery.nodeName( node, "script" ) ?
        scripts.push( node ) :
        node.type.match( rscriptType ) !== null && ...
}
// 脚本被收集后,默认情况下直接从结果中剔除
// 只有 keepScripts 为 true 时才会保留并允许后续执行

也就是说,无论keepScripts取什么值,script节点都会被单独抽出来收集。区别在于:keepScripts为false(默认)时这些脚本不会进入返回结果,调用者拿到的节点数组里根本没有script;而为true时,jQuery.ajax等内部调用方会把这些脚本交给jQuery.globalEval执行,后者会把脚本插入head再立即移除,从而在全局作用域跑起来。

还有一点值得注意:$(el).html(str)在内部调用parseHTML(str, el, false),所以通过html方法插入的脚本默认是不执行的。但$(el).append(str)在旧版本中走的是不同路径,行为存在版本差异,升级jQuery后出现"脚本突然不执行了"的问题多半源于此。理解这个参数的历史变化,排查这类诡异现象会快很多。

丢弃脚本不等于绝对安全:XSS防御的正确姿势

很多文章说"parseHTML默认不执行脚本所以是安全的",这个说法只对了一半。丢弃script确实堵住了最直接的执行通道,但XSS的载体远不止script标签。带有onerror属性的img标签、a标签的javascript:伪协议、style中的表达式等,都不依赖script标签就能触发攻击。看一个典型例子:

// 危险!虽然script被过滤了,但事件属性依然生效
var html = '<img src=x onerror="alert(document.cookie)">';
$( "#container" ).html( html ); // onerror 触发,XSS 成功

正确的防御思路是分层处理:第一层,绝不把不可信的富文本直接交给任何HTML解析入口,包括parseHTML和innerHTML;第二层,对必须渲染富文本的场景,先用DOMPurify这类专业的净化库过滤,再交给jQuery:

// 推荐做法:先净化再解析
var clean = DOMPurify.sanitize( untrustedHtml );
var nodes = jQuery.parseHTML( clean, document, false );
$( "#container" ).empty().append( nodes );

// 如果确实需要执行动态脚本,显式创建并插入
var s = document.createElement( "script" );
s.src = "https://example-cdn.ipipp.com/app.js";
document.head.appendChild( s );

此外,如果你确实需要keepScripts为true的场景(例如加载一段需要执行的HTML片段),务必保证这段字符串来源可信,最好来自自己的服务器而非用户输入或第三方接口。在审计代码时,全局搜索parseHTML、.html(和innerHTML三处调用点,逐个确认数据来源,是最有效的自查手段。默认拒绝执行脚本只是jQuery给你的一道保险,真正可靠的安全边界,永远建立在"对输入内容做净化"这条原则上。

jQuery.parseHTMLkeepScriptsXSS防御修改时间:2026-09-16 21:28:42

免责声明:已尽一切努力确保本网站所含信息的准确性。网站作品多为原创整理与精心创作,观点力求客观中立。本站旨在免费分享,内容仅供个人学习、研究或参考使用。若引用了第三方作品,版权归原作者所有。如内容涉及您的权益,请联系我们进行处理Email:chomcom@qq.com。
引用或转载本作品时,请注明当前出处:https://www.ipipp.com/html/0916/58199.html,基于非商业用途的前提下,欢迎转载或二创本作品。
内容垂直聚焦
专注技术核心技术栏目,确保每篇文章深度聚焦于实用技能。从代码技巧到架构设计,为用户提供无干扰的纯技术知识沉淀,精准满足专业提升需求。
知识结构清晰
覆盖从开发到部署的全链路。AI、前端、编程、数据库、服务器、建站、系统层层递进,构建清晰学习路径,帮助用户系统化掌握开发与运维所需的核心技术。
深度技术解析
拒绝泛泛而谈,深入技术细节与实践难点。无论是数据库优化还是服务器配置,均结合真实场景与代码示例进行剖析,致力于提供可直接应用于工作的解决方案。
专业领域覆盖
精准对应开发生命周期。从前端界面到后端编程,从数据库操作到服务器运维,形成完整闭环,一站式满足全栈工程师和运维人员的技术需求。
即学即用高效
内容强调实操性,步骤清晰、代码完整。用户可根据教程直接复现和应用于自身项目,显著缩短从学习到实践的距离,快速解决开发中的具体问题。
持续更新保障
专注既定技术方向进行长期、稳定的内容输出。确保各栏目技术文章持续更新迭代,紧跟主流技术发展趋势,为用户提供经久不衰的学习价值。