欢迎光临
我们一直在努力

JavaScript原型污染漏洞Prototype Pollution

文章目录

    • 原型链污染漏洞 Prototype Pollution
    • JavaScript语言的对象继承机制。
    • 防御方法
    • 扩展阅读

原型链污染漏洞 Prototype Pollution

原型污染(Prototype Pollution)是一种针对 JavaScript 运行时的注入攻击手法。攻击者利用该漏洞,能够篡改对象属性的默认值,从而干扰应用程序的正常业务逻辑,最终可能引发拒绝服务攻击(DoS)或远程代码执行(RCE)等严重安全风险。 下面是一个原型污染漏洞的示例:

function merge(target,source){
for (let key in source){
console.log("source", key)
if (key in source && key in target){
console.log("key", key)
merge(target[key], source[key])
}else {
console.log("key",key)
target[key]= source[key]
}
}
}

let o1 ={}
let o2 = JSON.parse('{"a": 1, "__proto__": {"b": 2}}')
console.log(o2)
merge(o1,o2)
console.log(o1.a, o1.b)//1 2
o3 ={}
console.log(o3.b)//2

在该示例中,merge 函数存在原型污染漏洞。攻击者通过构造 o2 对象,将 __proto__ 作为一个属性键(key)传入 merge 函数,导致 o1 的 __proto__ 字段被改写,从而为其原型链添加了属性 b: 2。由于 __proto__ 指向 Object.prototype,所有继承该原型的对象(如后续创建的 o3)都会自动拥有这个被注入的属性 b。这个过程就是原型链被污染的过程。

原型污染漏洞的根源在于 JavaScript 语言独特的对象继承机制以及其对象属性的公开性。

JavaScript语言的对象继承机制。

与 Java 语言通过类实现代码封装不同,JavaScript 语言中没有“类”的概念。函数、对象等数据结构都继承自原型对象 Object。每个对象实例都有两个特殊属性:prototype 和 __proto__。其中,__proto__ 属性指向该对象实例的原型对象(即它继承自哪个对象),而 prototype 属性则指向原型对象的构造方法。

当访问一个对象的属性时,如果在该对象实例本身没有找到该属性,JavaScript 引擎就会通过 __proto__ 属性沿着原型链向上查找,依次在它的原型对象中搜索,直到查找到最底层的 Object 对象(其 __proto__ 属性为 null)。因此,JavaScript 对象的属性查找过程形成了一条原型继承链。

在上面的示例中: 执行过代码之后,如果查看o3的原型,那么就会看到Object对象被添加了b属性。

console.log(o3.__proto__)
// [Object: null prototype] { b: 2 }

因所有对象都继承自原型对象Object,那么后续创建的其他对象,也会具有b属性:

const o4 = function() {};
class o5 extends o4 {};
console.log(o4.b) //2
console.log(o5.b) //2

此过程中,原型链(Prototype Chain)如下: o5.proto –> Function: o4.proto –> {}.proto –> Object:null

防御方法

  • 避免直接操作 proto 或原型,对 proto 或 constructor 进行过滤。
  • // 安全做法:避免动态设置 __proto__
    function safeMerge(target, source) {
    for (const key in source) {
    if (key === '__proto__' || key === 'constructor') {
    continue; // 忽略敏感属性
    }
    target[key] = source[key];
    }
    }

  • 使用 Object.create(null) 创建无原型对象 这种方法创建的对象不继承Object.prototype
  • const safeObj = Object.create(null); // 无原型链
    safeObj.name = "Safe";
    console.log(safeObj.toString); // undefined(无法访问 Object.prototype 的方法)

  • 冻结原型对象
  • // 冻结 Object.prototype
    Object.freeze(Object.prototype);

    // 尝试污染原型会失败(严格模式下报错)
    Object.prototype.isAdmin = true;
    console.log(Object.prototype.isAdmin); // undefined

  • 安全处理对象合并操作 在合并对象属性时,过滤敏感键(如 proto、constructor、prototype)。
  • function safeAssign(target, source) {
    Object.keys(source).forEach(key => {
    if (key in Object.prototype) return; // 忽略原型上的键
    target[key] = source[key];
    });
    }

  • 使用 Map 替代普通对象
  • const map = new Map();
    map.set("isAdmin", true);
    console.log(map.get("isAdmin")); // true
    console.log(map.isAdmin); // undefined(无法通过原型链污染)

  • 使用安全的第三方库 避免手写不安全的对象操作,选择经过安全审计的库(如 Lodash 的 _.merge 默认过滤原型键)。
  • onst _ = require('lodash');
    const safeObj = _.merge({}, maliciousPayload); // 自动忽略 __proto__

  • 启用严格模式(Strict Mode) 关键点:严格模式下,修改只读属性(如冻结后的原型)会抛出错误。
  • 'use strict';
    Object.prototype.isAdmin = true; // 报错:Cannot add property isAdmin

  • Schema validation of JSON input NPM 上有多个库(例如 ajv)为 JSON 数据提供了模式验证功能。可以在 JSON Schema 规则中添加 additionalProperties: false 来禁止额外的属性。 结合最开始的例子,关键代码 let o2 = JSON.parse('{"a": 1, "__proto__": {"b": 2}}') 之所以使用 JSON.parse() 的方式构造对象,是因为如果直接写作 o2 = {"a": 1, "__proto__": {"b": 2}},会把 o2 的 __proto__ 直接赋值为 {"b": 2},而不是在 o2 指向的原型对象中添加属性。通过 JSON Schema 验证模式,可以阻断这种绕过过程,防止恶意属性的添加。
  • 扩展阅读

    [1] JS应用安全漏洞利用详解:从原型链污染到全栈RCE [2] Prototype pollution [3] 什么是原型污染?如何防止原型污染

    赞(0)
    未经允许不得转载:171主机测评 » JavaScript原型污染漏洞Prototype Pollution
    分享到: 更多 (0)

    评论 抢沙发

    • 昵称 (必填)
    • 邮箱 (必填)
    • 网址