Web安全
基础漏洞
01前端基础【HTML】
02前端基础【CSS】
03后端基础【PHP速通】
04后端基础【PHP面向对象】
05MySQL基础操作
06前后端联动【代码练习】
07SQL注入【1】
07SQL 注入【2】
08SQL注入 Labs
08SQL注入速查表
09XSS
09跨站脚本攻击【XSS】
09XSS Labs
10跨站请求伪造【CSRF】
11服务器端请求伪造【SSRF】
12XML 外部实体注入【XXE】
13代码执行漏洞
14命令执行漏洞
15文件包含漏洞
16文件上传漏洞
17反序列化漏洞
18业务逻辑漏洞
19未授权访问漏洞集合
20跨源资源共享【CORS】
21SSTI模板注入
22并发漏洞
23点击劫持【Clickjacking 】
24请求走私
25路径遍历
26访问控制
27身份验证漏洞
28WebSocket
29Web缓存中毒
30HTTP 主机头攻击
31信息泄露漏洞
32原型污染
33NoSQL注入
API 安全
01web应用程序
02HTTP协议
03API概述
04分类类型
05交换格式
06身份验证
07常见API漏洞
08crAPI靶场
09JWT
10OAuth 2.0身份验证
11GraphQL【1】
11GraphQL【2】
12DVGA靶场
13服务器端参数污染
14API文档
15API Labs
16OAuth Labs
17GraphQL API Labs
18JWT Labs
小程序
小程序抓包
数据库
MySQL
Oracle
MongoDB
Redis
PostgreSQL
SQL server
中间件
Nginx
Apache HTTP Server
IIS
Tomcat
框架
ThinkPHP
Spring
Spring Boot
Django
访问控制
-
+
首页
32原型污染
要理解**原型污染(Prototype Pollution)**,首先需要掌握 JavaScript 的**原型(Prototype)与原型链(Prototype Chain)** 机制 —这是 JS 实现继承的核心,也是原型污染产生的根源。 ## JS 的原型与原型链 在 JS 中,**几乎所有对象都是通过构造函数创建的**,而每个构造函数都有一个 prototype 属性(原型对象);同时,每个实例对象都有一个隐式的 [[Prototype]] 指针(可通过 __proto__ 访问,非标准但浏览器普遍支持),指向其构造函数的 prototype。 当访问一个对象的属性时,JS 会先在对象自身查找;若找不到,则沿着 [[Prototype]] 向上遍历**原型链**,直到找到属性或遍历到顶层的 Object.prototype(其 [[Prototype]] 为 null)。 例如: ```javascript // 1. 构造函数 function Person(name) { this.name = name; // 实例自身属性 } // 2. 给构造函数的原型添加方法(所有实例共享) Person.prototype.sayHi = function() { console.log(Hi, ${this.name}); }; // 3. 创建实例 const alice = new Person("Alice"); alice.sayHi(); // 输出 "Hi, Alice" // 原理:alice 自身没有 sayHi,沿 alice.__proto__ → Person.prototype 找到 sayHi // 4. 顶层原型:Object.prototype console.log(alice.toString()); // 输出 "[object Object]" // 原理:alice.__proto__.__proto__ → Object.prototype,toString 是 Object.prototype 的方法 ``` 所以: - **所有对象最终都继承自 Object.prototype**(除非用 Object.create(null) 创建无原型对象)。 - 若修改某个原型对象(如 Object.prototype),则**所有继承自该原型的对象都会受到影响**—— 这正是原型污染的关键。 ## 什么是原型污染? **原型污染是指:攻击者通过未授权的方式,修改了对象的原型(尤其是顶层原型 Object.prototype),导致所有继承自该原型的对象都被 “污染”,从而引发一系列安全问题或逻辑漏洞。** 简单来说,就是利用 JS 原型链的特性,强行给原型对象注入恶意属性或方法,让所有依赖该原型的对象 “被动继承” 这些恶意内容。 ## 实现方式 原型污染的核心是**修改原型对象的属性**,常见途径有 3 种: **1. 通过 __proto__ 直接修改** __proto__ 是实例对象访问其原型的 “入口”,若攻击者能控制对象的键名(如用户输入作为键),就可以通过 obj["__proto__"] 或 obj.__proto__ 直接修改原型。 示例:污染 Object.prototype ```javascript // 1. 初始状态:空对象没有 isAdmin 属性 const normalObj = {}; console.log(normalObj.isAdmin); // 输出 undefined // 2. 攻击者通过 __proto__ 污染 Object.prototype const attackObj = {}; attackObj.__proto__.isAdmin = true; // 等价于 Object.prototype.isAdmin = true // 3. 所有对象(继承自 Object.prototype)都被污染 console.log(normalObj.isAdmin); // 输出 true(原本没有,现在从原型继承) const anotherObj = new Array(); // 数组也继承自 Object.prototype console.log(anotherObj.isAdmin); // 输出 true ``` **2. 通过 prototype 属性修改** 构造函数的 prototype 属性直接指向原型对象,若攻击者能控制构造函数,可通过 Constructor.prototype 修改原型。 示例:污染 Array.prototype ```javascript // 攻击者修改 Array 的原型,注入恶意方法 Array.prototype.stealData = function() { console.log("窃取数组数据:", this); }; // 所有数组实例都被动继承该方法 const myArray = [1, 2, 3]; myArray.stealData(); // 输出 "窃取数组数据:[1,2,3]" ``` **3. 通过 Object.setPrototypeOf 方法** Object.setPrototypeOf(target, proto) 是 JS 标准方法,用于设置 target 的原型为 proto。若 target 是原型对象,或 proto 包含恶意内容,会导致污染。 ```javascript const maliciousProto = { backdoor: true }; // 将 Object.prototype 的原型设为恶意原型(虽不常见,但可污染) Object.setPrototypeOf(Object.prototype, maliciousProto); const testObj = {}; console.log(testObj.backdoor); // 输出 true(沿原型链找到 maliciousProto) ``` ## 危害 原型污染的危害程度取决于攻击场景,轻则破坏应用逻辑,重则配合其他漏洞实现**远程代码执行(RCE)**。 **1. 篡改全局数据 / 权限** 如示例中污染 Object.prototype.isAdmin = true,可能导致应用误判所有用户为 “管理员”,绕过权限检查。 **2. 破坏应用逻辑** 若污染 Object.prototype.toString 等原生方法,会导致依赖这些方法的功能失效: ```javascript // 污染 toString 方法 Object.prototype.toString = function() { return "被篡改了!"; }; console.log([1,2,3].toString()); // 输出 "被篡改了!"(原本应为 "1,2,3") console.log(new Date().toString()); // 输出 "被篡改了!"(原本应为日期字符串) ``` **3. 配合漏洞实现 RCE** 原型污染本身不直接执行代码,但可作为 “前置漏洞”,结合其他特性(如 eval、Function 构造函数、模板字符串注入等)触发 RCE。 例如,若应用中有如下代码(使用用户输入动态执行函数): ```javascript function execute(code) { return eval(code); // 危险:直接执行字符串代码 } // 攻击者污染 Object.prototype,注入恶意代码 Object.prototype.payload = "console.log('恶意代码执行')"; // 若应用中存在代码拼接(如 execute("obj.payload")),则会执行恶意代码 execute("this.payload"); // 输出 "恶意代码执行" ``` **4. 绕过安全机制** 部分安全工具(如 XSS 过滤器)依赖原生对象方法,原型污染可能导致过滤器失效,间接助长其他攻击。 ## 攻击场景 原型污染通常发生在**用户输入能直接控制对象键名或属性赋值**的场景,常见场景包括: | 场景 | 原理 | | ----------------- | ------------------------------------------------------------ | | 不安全的对象合并 | 如使用 $.extend(true, target, source)(jQuery 旧版本)、自定义 merge 函数,若 source 包含 __proto__ 键,会污染原型。 | | 序列化 / 反序列化 | 如 JSON.parse 配合 reviver 参数、unserialize 函数(如 PHP-JS 交互时),攻击者可构造包含 __proto__ 的序列化数据。 | | 用户输入作为键名 | 如 obj[userInputKey] = userInputValue,若 userInputKey 为 __proto__,则修改原型。 | | 第三方库漏洞 | 历史上多个知名库存在原型污染漏洞(如 Lodash < 4.17.19、jQuery < 3.4.0、Moment.js 等),攻击者可利用库的接口触发污染。 | ## 防护措施 防御的核心是**阻止未授权的原型修改**,针对不同场景可采取以下措施: **1. 过滤敏感键名** 若用户输入作为对象键名,需过滤 __proto__、prototype 等敏感关键词,避免修改原型: ```javascript function safeSet(obj, key, value) { // 过滤敏感键,直接忽略或报错 if (key === "__proto__" || key === "prototype") { throw new Error("非法键名!"); } obj[key] = value; } const obj = {}; safeSet(obj, "__proto__", { isAdmin: true }); // 抛出错误,避免污染 ``` **2. 使用 Object.create(null) 创建无原型对象** Object.create(null) 会创建一个**没有任何原型**的对象(其 [[Prototype]] 为 null),不会继承 Object.prototype,因此即使被污染也不会影响全局: ```javascript // 无原型对象,不受 Object.prototype 污染影响 const safeObj = Object.create(null); Object.prototype.isAdmin = true; console.log(safeObj.isAdmin); // 输出 undefined ``` **3. 禁止直接修改原型** 避免使用 __proto__ 赋值(可通过 Object.freeze(Object.prototype) 冻结顶层原型,使其无法被修改): ```javascript // 冻结 Object.prototype,禁止添加/修改属性 Object.freeze(Object.prototype); Object.prototype.isAdmin = true; // 无效(严格模式下会报错) ``` 慎用 Object.setPrototypeOf,若必须使用,需严格校验 proto 参数。 **4. 使用安全的对象合并方法** 避免使用老旧版本的 jQuery($.extend(true) 存在漏洞),升级到最新版。 自定义 merge 函数时,需递归检查键名,跳过敏感键: ```javascript function safeMerge(target, source) { for (const key in source) { // 跳过敏感键,避免污染原型 if (key === "__proto__" || key === "prototype") continue; const targetVal = target[key]; const sourceVal = source[key]; // 若均为对象且非 null,递归合并(避免深污染) if (typeof targetVal === "object" && targetVal !== null && typeof sourceVal === "object" && sourceVal !== null) { safeMerge(targetVal, sourceVal); } else { target[key] = sourceVal; } } return target; } ``` **5. 及时更新依赖库** 定期扫描项目依赖(如使用 npm audit),修复第三方库中的原型污染漏洞(如 Lodash、jQuery 等)。 **6. 启用严格模式(Strict Mode)** 在 JS 文件头部添加 "use strict";,严格模式下部分不安全操作(如意外修改原型)会直接报错,便于提前发现问题。
毛林
2025年9月10日 18:22
转发文档
收藏文档
上一篇
下一篇
手机扫码
复制链接
手机扫一扫转发分享
复制链接
Markdown文件
PDF文档(打印)
分享
链接
类型
密码
更新密码