jstips 第 42 期:预防 Unapply 攻击——用 Object.freeze 冻结内置原型

发布时间:2026/10/8 14:14:32
jstips 第 42 期:预防 Unapply 攻击——用 Object.freeze 冻结内置原型 教程【免费下载链接】jstipsThis is about useful JS tips!项目地址https://gitcode.com/gh_mirrors/js/jstips点击查看免费下载本文基于 jstips 仓库_posts/es_ES/javascript/2016-02-11-preventing-unapply-attacks.mdTip #42作者 emars整理扩充。它讲解了一种名为 unapply attack 的原型污染手法——外部代码通过改写Array.prototype.concat等内置方法窃取并篡改bindpolyfill 闭包中绑定的参数从而让依赖 polyfill 的应用崩溃。读完本文你将掌握这种攻击的完整复现代码、攻击的底层生效原理以及使用Object.freeze冻结内置原型Object.prototype、Array.prototype、Function.prototype这一防御方案同时理解为什么它只能算缓解措施、而非真正的安全边界。什么是 Unapply 攻击JavaScript 的内置原型builtin prototypes是全局可写的任何代码都可以在运行时改写Object.prototype、Array.prototype、Function.prototype上的方法。本 tip 指出一旦外部代码改写了内置原型它就可以通过重写代码来暴露expose和修改change原本被闭包捕获的绑定参数进而破坏那些依赖 ES5 polyfill 方法才能正常工作的应用——这是一个可能造成严重破坏的问题。Unapply 这个名字暗示了攻击的核心动作正常流程是fn.apply(thisArg, args)把参数应用到函数上而攻击者则试图反向操作——把已经通过闭包绑定bind进去的参数从函数内部剥离出来。攻击复现bind polyfill 与 unapplyAttack原文档给出了两段关键代码。第一段是一个典型的bindpolyfill// example bind polyfill function bind(fn) { var prev Array.prototype.slice.call(arguments, 1); return function bound() { var curr Array.prototype.slice.call(arguments, 0); var args Array.prototype.concat.apply(prev, curr); return fn.apply(null, args); }; }它做的事情是调用bind(fn, a, b, ...)时用Array.prototype.slice.call(arguments, 1)把除fn之外的参数截取为prev数组并通过闭包保存返回的bound函数在被调用时再把本次传入的参数截取为curr通过Array.prototype.concat.apply(prev, curr)把预绑定参数prev和运行时参数curr拼接成完整的args最后fn.apply(null, args)执行原函数。注意第 3 步拼接动作依赖Array.prototype.concat这个内置方法而prev这个闭包变量会作为concat的接收者this被传入。第二段是攻击函数// unapply-attack function unapplyAttack() { var concat Array.prototype.concat; Array.prototype.concat function replaceAll() { Array.prototype.concat concat; // restore the correct version var curr Array.prototype.slice.call(arguments, 0); var result concat.apply([], curr); return result; }; }攻击分两步先用var concat Array.prototype.concat把原始的concat方法缓存到局部变量再给Array.prototype.concat赋上一个新的replaceAll函数其中内部立即把Array.prototype.concat恢复为原始版本restore the correct version然后用缓存的原始concat以[]为接收者、仅携带运行时参数curr完成拼接。攻击为什么会让 bind 崩溃关键差异在于replaceAll内部使用的是concat.apply([], curr)接收者是空数组[]而不是polyfill 闭包里的prev。于是在unapplyAttack()执行之后任何一个bind出来的函数第一次被调用时其内部的Array.prototype.concat.apply(prev, curr)拿到的已经是被替换的replaceAllreplaceAll内部把concat恢复到原始版本但调用时接收者被换成了[]绑定的prev数组被直接丢弃discards theprevarray from the bind结果拼接出的args只剩下curr甚至因为prev缺失导致参数结构错乱——原文档明确指出使用 unapply 攻击后紧随其后的第一次.concat调用就会抛出错误the first concat call following using the unapply attack will throw an error。换句话说攻击者不需要知道prev的具体内容只要在关键时刻替换一次concat就能让所有基于闭包绑定参数的功能失效——这正是重写代码以暴露和修改绑定参数的含义。防御方案Object.freeze 冻结内置原型Object.freeze()能让对象变为不可变immutable既不能新增属性也不能删除或重新定义属性现有属性的值也不可写。把这个能力用在原型对象上就相当于给内置原型上了一把锁任何后续的覆盖写入都会被静默拒绝非严格模式下或抛出TypeError严格模式下。原文档给出的完整防御代码是一个立即执行函数IIFE(function freezePrototypes() { if (typeof Object.freeze ! function) { throw new Error(Missing Object.freeze); } Object.freeze(Object.prototype); Object.freeze(Array.prototype); Object.freeze(Function.prototype); }());逐行解读if (typeof Object.freeze ! function) { throw new Error(Missing Object.freeze); }先做能力检测。Object.freeze是 ES5 引入的 API在非常老旧的环境中可能不存在此时直接抛出错误避免静默失败Object.freeze(Object.prototype)冻结Object.prototype防止所有对象的原型链顶层被改写Object.freeze(Array.prototype)冻结Array.prototype直接封死concat、slice等数组方法的替换路径Object.freeze(Function.prototype)冻结Function.prototype阻止apply、call、bind等函数方法被篡改。三段冻结互补Object.prototype管所有对象的公共祖先Array.prototype管攻击最常见的落点concat/sliceFunction.prototype管函数调用机制本身。建议在应用启动、任何第三方脚本加载之前尽早执行这段代码让冻结先于污染。部署方式很简单把它放到入口脚本的顶部或在script标签中以立即执行形式加载。深入 Object.freeze不可变对象的三个层次jstips 仓库中另一篇 tip_posts/en/javascript/2018-11-25-creating-immutable-objects-in-native-javascript.md系统介绍了原生 JavaScript 制造不可变对象的三个 API可以帮助你理解为什么选freeze而不是seal或preventExtensionsAPI阻止新增属性阻止删除属性阻止重定义描述符阻止修改属性值Object.preventExtensions()✅❌❌❌Object.seal()✅✅✅❌Object.freeze()✅✅✅✅Object.preventExtensions()只是禁止向对象添加新属性且不可逆Object.seal()在此基础上禁止删除属性和重定义属性描述符但属性值仍可写Object.freeze()是三者中约束最彻底的加上属性值不可写non-writable这也是冻结原型所必需的能力——否则攻击者虽然无法替换concat却仍可能改写其内部的默认行为。可以用Object.isFrozen(Object.prototype)、Object.isFrozen(Array.prototype)等方法来验证冻结是否生效。另外结合仓库中 _posts/en/javascript/2016-01-07-use-strict-and-get-lazy.md 这篇 tip 的提醒在严格模式use strict下对只读属性如被冻结原型上的方法执行赋值会直接抛出TypeError错误把静默失败变成响亮失败更便于在开发阶段第一时间发现原型被篡改的迹象。重要提醒冻结不是真正的安全边界原文档在结尾给出了冷静而重要的限定英文原文见 _posts/en/javascript/2016-02-11-preventing-unapply-attacks.md虽然这个概念被称为 unapply attack因为某些代码能够访问通常不在其作用域内的闭包但把它当作安全特性security feature来看待是错误的原因之一是如果攻击者本身就拥有代码执行能力他完全可以在冻结发生之前就扩展原型——冻结只能封住冻结之后的改写封不住冻结之前的污染原因之二是攻击者即便不修改原型也仍然可以利用各种语言特性如eval、with、动态作用域去读取所有作用域内的变量相对更强的隔离方案是ECMA 模块ES Modules模块系统提供基于 realm 的隔离realm-based isolation比冻结原型的防御力度大得多但它依然无法彻底解决第三方脚本带来的问题第三方脚本往往需要直接访问全局环境天然难以完全隔离。因此正确的姿势是把Object.freeze冻结内置原型当作纵深防御中的一环让意外污染和常规攻击更难成功而不是可以替代代码审查、沙箱隔离或模块边界的万能安全锁。在 jstips 仓库中的对应资源本文对应的原始 tip 在仓库中提供了四种语言版本内容互为镜像可以对照阅读西班牙语本篇依据_posts/es_ES/javascript/2016-02-11-preventing-unapply-attacks.md英语原始版本_posts/en/javascript/2016-02-11-preventing-unapply-attacks.md简体中文_posts/zh_CN/javascript/2016-02-11-preventing-unapply-attacks.md繁体中文_posts/zh_TW/javascript/2016-02-11-preventing-unapply-attacks.md该 tip 在项目 README.md 的 Tips 列表中登记为第 42 条42 - Preventing Unapply Attacks属于javascript分类。相关延伸阅读还包括上文提到的不可变对象专题Tip #74与严格模式专题Tip #07它们共同构成了理解原型安全 不可变性的完整知识链。赞分享教程【免费下载链接】jstipsThis is about useful JS tips!项目地址https://gitcode.com/gh_mirrors/js/jstips点击查看免费下载相关推荐预防 Unapply 攻击冻结 JavaScript 内置原型抵御原型链篡改jstips 第 42 期预防 Unapply 攻击冻结 JavaScript 内置原型抵御原型链篡改jstips 第 42 期 本文源自 jstips 开源仓库 README教程mini-vue中的响应式对象冻结Object.freeze处理mini vue中的响应式对象冻结Object.freeze处理 在开发响应式应用时你是否遇到过需要保护对象不被意外修改的场景是否想过如何在mini vu前端教程minikube v1.24.0 time-to-k8s 基准测试解读从零到成功部署 Kubernetes 的耗时测量与对比minikube v1.24.0 time to k8s 基准测试解读从零到成功部署 Kubernetes 的耗时测量与对比 minikube 项目在发布每个教程上一篇Windows热键侦探终极指南帮你找回被偷走的快捷键下一篇终极文档下载神器3步免费下载百度文库、原创力文档等30平台内容创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

关于本文作者

来自尧图内容编辑团队

尧图内容编辑团队 内容团队

尧图内容编辑团队

本文由尧图网络内容编辑团队执笔。团队由资深项目经理、前端工程师与设计师组成,所有内容均来自亲手交付的真实项目,先讲清问题、再给出可落地的解法。尧图深耕北京网站建设十年,服务过京华建材集团、智造科技等各行业客户,把一线经验沉淀为可复用的行业观察。

  • 十年建站经验,覆盖建材、制造、服务、文创等
  • 项目经理把关选题与事实准确性
  • 工程师与设计师联合撰写专业细节
  • 统一编辑规范,保证文风与排版一致
  • 每月复盘转化数据,迭代选题方向

延伸阅读

相关资讯与近期热门内容

深度阅读推荐

建站决策前值得细读的三篇

网站改版的5个关键决策
2024-08-12

网站改版的5个关键决策

什么时候该改版、改到什么程度、如何避免流量掉光,京华建材集团改版复盘给出答案。

获取专属建站方案

看完文章,把您的行业与预算告诉我们,免费获取一份量身定制的官网建设方案与报价。

立即免费咨询