首页 > 其他分享 >js原型链污染详解

js原型链污染详解

时间:2023-02-22 12:45:28浏览次数:37  
标签:__ name proto Object js 详解 原型

前言

之前打某湖论剑,两道js的题,给我整懵逼了,发现以前都没对js做过多少研究,趁着被毒打了,先研究一波js原型链,未雨绸缪。

基础

protype

首先我们研究js原型链,得搞明白原型是什么,这里借用p神的举的一个例子:
在javascript中,我们定义一个类,需要以定义“构造函数”的方式来定义:

function Foo() {
    this.bar = 1
}

new Foo()

Foo()函数的内容就是构造函数的内容,this.bar是Foo的一个属性,学过c++的应该很容易理解,而且后面对原型链的利用也可以仿造c++类的思想来理解。

一个类必然有方法,我们可以在构造函数里定义方法:

function Foo() {
    this.bar = 1
    this.show = function() {
        console.log(this.bar)
    }
}

在js里,这样定义有一个特点,就是这个方法并不是绑定在类上,而是每创建一个对象,这个方法就会定义一次,这样就很浪费资源,我们要让它只定义一次,就需要用到原型prototype,这个prototype可以认为是一个类的属性,通过类创建的对象都将"继承"prototype的内容。

function Foo() {
    this.bar = 1
}

Foo.prototype.show = function show() {
    console.log(this.bar)
}

__proto__

那么__proto__是干什么用的?原来由类实例化的对象是无法直接访问到类的原型也就是prototype,我们可以看到foo没有prototype

image

这时__proto__就是对象访问类原型的媒介了

image

以上大概就是原型的内容,读者把原型的概念弄懂后再去看原型链污染会有不一样的结果

原型链继承

以上讲的prototype在js里其实主要是用来实现继承机制,这个继承跟c++的继承不太一样,js的继承可以改掉Object类导致原型链污染,而c++的继承是可以对父类的方法进行进行重写或重载,但不会直接就把父类给改写了,因为这个特性,这才有了原型链污染的诞生。这里举个例子:

function Father() {
    this.first_name = 'Donald'
    this.last_name = 'Trump'
}

function Son() {
    this.first_name = 'Melania'
}

Son.prototype = new Father()

let son = new Son()
console.log(`Name: ${son.first_name} ${son.last_name}`)

这里Son继承了Father的last_name

image

这里通过__proto__给Object添了个last_name

image

这里推导一下:
son.__proto__ == Son.prototype
son.__proto__.__proto__ == Father.prototype
son.__proto__.__proto__.__proto__ == Object
所以后面我们new的a,它有一个last_name是继承Object的,这里有趣的是,改写了Object后,Father.last__name == john,但是son.last_name == Trump,直接看Father函数,其实它的last_name并没有变化

image

这就有趣了,但重新new Father,它的last_name其实也是没变的

image

但我们还是成功污染了的,Object多了个last_name属性,此后新建的类都将继承这个属性,在某些地方是会造成危害的

原型链污染实例

在什么地方我们可以使用原型链污染?其实当有我们可以控制的"键名",并存在有赋值修改操作的地方,我们可以实现这个操作,这里举个js的merge函数经典例子:

function 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]
        }
    }
}

以上是一个简单的合并函数,我们可以看到target[key] = source[key],其实这个地方就存在原型链污染,如果key是__proto__的话,是不是就能修改到Object,这里给个例子:

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

o3 = {}
console.log(o3.b)

image

这里我们合并成功了,但是并没有污染到Object,原因是__proto__没有被当成键名,而是当成原型,也就是遍历键名的时候只有a,b是键名。

为了让__proto__也被当成键名,我们可以把o2的值设置成json的格式,遍历json的时候,__proto__就会被当成键名,从而改写Object,例子如下:

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

o3 = {}
console.log(o3.b)

成功改写Object,导致o3有了b属性

image

总结

以上就是js原型链的基础内容,把基础打好,之后遇见原型链的题将会成长很快。

标签:__,name,proto,Object,js,详解,原型
From: https://www.cnblogs.com/F12-blog/p/17141216.html

相关文章

  • createrepo 命令详解
    createrepo命令用于创建yum源(软件仓库),即为存放于本地特定位置的众多rpm包建立索引,描述各包所需依赖信息,并形成元数据。语法:createrepo[option]<directory>参数选项......
  • vue3 ThreeJS 引入obj模型过暗的问题
      当我单纯地用MTLLoader引入材质,OBJLoader引入模型并添加到场景中时, 发现模型非常得暗. 需要将环境光的强度设置到3.5左右看起来才比较正常. 但正常情况下环境光......
  • js操作shadow-root内的DOM元素
    其实就是documentfragment元素,就是动态生成的文档碎片元素。1,项目中在DOM结构里遇到了shadow-root(open),用JS方法无法直接获取其内的DOM元素2、shadow DOMWebcompon......
  • iOS Swift开发中JSON对象/JSON字符串/Data的互转
    https://www.jianshu.com/p/712bb9a60184 JSON(对象)----->JSON字符串//JSON--->data--->JSON字符串letdata=try?JSONSerialization.data(withJSONObject:json......
  • 浏览器对象详解
    1、浏览器对象模型参考资料:知识整理——浏览器对象模型BOM:BrowserObjectModel(浏览器对象模型),浏览器模型提供了独立于内容的、可以与浏览器窗口进行滑动的对象结构,就是......
  • js - div 显隐切换
    <divid="divActualProductionLine"></div> if(viewModel.EDNMain.Plant!=null&&viewModel.EDNMain.Plant.indexOf("1201")){ $("#divActualProductionLine").c......
  • Nextjs Contentful GraphQL Vercel Edges
    配置contentful创建免费账号根据提示进行操作,ContentModel-创建页面属性模板(personalWebsite)contententry - 根据属性模板,添加内容,最后发布APIcall-......
  • 模型压缩-剪枝算法详解
    一,前言1.1,模型剪枝定义二,深度神经网络的稀疏性2.1,权重稀疏2.2,激活稀疏2.3,梯度稀疏2.4,小结三,结构化稀疏3.1,结构化稀疏分类3.1.1,channel剪枝3.1.2,阶段......
  • JS创建对象的三种方法(转)
    转自:JS创建对象的三种方法在JavaScript中,对象是一组无序的相关属性和方法的集合。所有的实物都是对象,例如,字符串、数值、数组、函数等。下面我会介绍三种创建对象的方法......
  • 详解Reflect:Reflect和Object的异同,Reflect的一些内置方法以及方法注意点
    ReflectReflect拥有Object对象的一些内部方法,某些方法会同时在Object对象和Reflect对象上部署,也就是说Object对象上存在的方法,通过Reflect也可以访问到。单论Reflect的话......