写点什么

JavaScript 原型链污染探讨

  • 2024-10-14
    福建
  • 本文字数:1599 字

    阅读完需:约 5 分钟

如果你想弄明白什么怎样才可以实现 JavaScript 的原型链污染,那么你首先需要弄清楚两个东西,那就是__proto__prototype


到底什么才是__proto__prototype?


那我们先来看看比较官方的说法吧:


__proto__:是每个对象的隐藏属性,指向创建该对象的构造函数的原型对象(prototype)。它是对象用于继承属性和方法的机制。它是一个对象。所有的对象都可以通过__proto__ 来访问它的原型,进而实现原型链查找。
复制代码


prototype是函数(特别是构造函数)特有的属性,它用于定义由该构造函数创建的实例共享的属性和方法。prototype通常用来定义构造函数的实例方法。当我们创建一个新对象时,该对象会继承其构造函数 prototype上的属性和方法。
复制代码


const obj = {};console.log(obj.__proto__ === Object.prototype); // true
复制代码


在这个例子中,obj是通过Object构造函数创建的,所以obj.__proto__指向Object.prototype


那我们可以这样去理解,prototype 是类通有的属性,当类被实例化为对象后,对象会拥有 prototype 中的属性和方法。当对象想去访问类的原型时用__proto__属性来访问类的原型。


prototype 链


了解了__proto__prototype之后,那我们就要去深入了解 JavaScript 的继承链,在 JavaScript 中,每个对象都有一个指向其原型的内部链接(即__proto__),这个原型本身也是一个对象,通常还有自己的原型,这样就形成了一条原型链。当你访问一个对象的属性时,JavaScript 会沿着这条原型链逐级查找,直到找到该属性或者原型链的顶端(即 Object.prototype)。


function Animal(name) {  this.name = name;}
function Something() { this.speak = console.log(this.name + ' makes a beautiful sound.');};
const dog = new Animal('Dog');dog.speak; // 输出: 'Dog makes a beautiful sound.'
复制代码


实例化 Animal 类创建了 dog 对象,当访问 speak 属性时,在 dog 中寻找不到时,会在dog.__proto__.__proto__中寻找,发现dog.__proto__.__proto__中有 speak 属性,也就是Something.prototype中存在 speak 属性。也就是说 JavaScript 使用 prototype 链实现继承机制。


prototype 链污染(原型链污染)


那我们学习继承、prototype 链不就是为了进行链子污染,然后得到我们想得到的东西吗?


既然dog.__proto__.__proto__指向创建该对象的构造函数的原型对象(Something.prototype)。那么我们修改dog.__proto__.__proto__的内容是不是可以进而实现了修改 Something 类。


示例:


function Animal(name) {    this.name = name;}
function Something() { this.speak = console.log(this.name + ' makes a beautiful sound.');};
const dog = new Animal('Dog');
dog.__proto__.speak = console.log('修改成功');
dog.speak;
复制代码



通过这个结果我们可以看出我们轻易的将dog.__proto__.__proto__的 speak 值进行了更改。我们先将这个继承链给搞出来:


dog -> Animal.prototype -> Something.prototype -> Object.prototype -> null
复制代码


那么攻击者如果通过一个入口点,控制并修改了一个对象的原型,那么将可能影响所有和这个对象来自同一个类、父类直到 Object 类的对象。


典型的便是 merge 操作导致原型链污染:


javascriptfunction merge(target, source) {    for (let key in source) {        if (key in source && key in target) {            merge(target[key], source[key])        } else {            target[key] = source[key]        }    }}
let object1 = {}let object2 = JSON.parse('{"a": 1, "__proto__": {"b": 2}}')merge(object1, object2)console.log(object1.a, object1.b)
object3 = {}console.log(object3.b)//1 2//2
复制代码


在 JSON 解析的情况下,__proto__会被认为是一个真正的“键名”,而不代表“原型”,所以在遍历 object2 的时候会存在这个键。


文章转载自:weljoni

原文链接:https://www.cnblogs.com/weljoni/p/18461552

体验地址:http://www.jnpfsoft.com/?from=infoq

用户头像

还未添加个人签名 2023-06-19 加入

还未添加个人简介

评论

发布
暂无评论
JavaScript原型链污染探讨_JavaScript_不在线第一只蜗牛_InfoQ写作社区