js原型链污染(超详细)
js创建对象的三种方法 :
普通创建
var person={name:'lihuaiqiu','age','19'} var person={} //创建空对象
构造函数方法创建
function person(){this.name="liahuqiu";this.test=function () {return 23333; } } person.prototype.a=3; web=new person(); console.log(web.test()); console.log(web.a)
通过object创建
var a=new Object(); a.c=3 console.log(a.c)
函数即对象(这里的函数有点类似于一个类)
prototype和proto用法
对象.proto=构造器(构造函数).prototype
构造器.prototype其实也是一个对象,为构造函数的原型对象,同样有__proto__
属性,一直通过原型链__proto__
最终可找到null。
我们可以通过Foo.prototype来访问Foo类的原型,但Foo实例化出来的对象,是不能通过prototype访问原型的。这时候,就该__proto__
登场了。
一个Foo类实例化出来的foo对象,可以通过foo.__proto__
属性来访问Foo类的原型,也就是说:
foo.__proto__ == Foo.prototype
这里类Foo在实例化时,便可调用prototype中的方法 所以,总结一下:
prototype是一个类的属性,所有类对象在实例化的时候将会拥有prototype中的属性和方法 一个对象(foo)的__proto__
属性,指向这个对象所在的类(Foo)的prototype属性
这个特性被用来实现JavaScript中的继承机制
比如:
function Father() {this.first_name = 'Donald'this.last_name = 'Trump'
}function Son() {this.first_name = 'Melania'
}Son.prototype = new Father()//这里Son.prototype意思是Son的父类,let son = new Son()
console.log(`Name: ${son.first_name} ${son.last_name}`)
Son类继承了Father类的last_name属性,最后输出的是Name: Melania Trump。
(这里的继承机制是主动继承,也就是子类可以选择主动去继承父类,和php中的extends是一样的,子类去extends父类)
总结一下,对于对象son,在调用son.last_name的时候,实际上JavaScript引擎会进行如下操作:
在对象son中寻找last_name 如果找不到,则在son.__proto__
中寻找last_name 如果仍然找不到,则继续在son._proto_
.__proto__
中寻找last_name 依次寻找,直到找到null结束。比如,Object.prototype的proto就是null
JavaScript的这个查找的机制,被运用在面向对象的继承中,被称作prototype继承链。
每个构造函数(constructor)都有一个原型对象(prototype) 对象的__proto__
属性,指向类的原型对象prototype JavaScript使用prototype链实现继承机制
原型组成的链,对象的__proto__
是原型,而原型也是一个对象,也有__proto__
属性,原型的__proto__
又是原型的原型,就这样可以一直通过__proto__
向上找,这便是原型链,当向上找找到Object
的原型的时候,这条原型链便算结束了。
原型链污染是什么
第一章中说到,foo.proto指向的是Foo类的prototype。那么,如果我们修改了foo.proto中的值,是不是就可以修改Foo类呢?
做个简单的实验:
// foo是一个简单的JavaScript对象
let foo = {bar: 1}// foo.bar 此时为1
console.log(foo.bar)// 修改foo的原型(即Object)
foo.__proto__.bar = 2// 由于查找顺序的原因,foo.bar仍然是1
console.log(foo.bar)// 此时再用Object创建一个空的zoo对象
let zoo = {}// 查看zoo.bar
console.log(zoo.bar)
最后,虽然zoo是一个空对象{},但zoo.bar的结果居然是2:
123456789101112131415161718
原因也显而易见:因为前面我们修改了foo的原型foo.__proto__
.bar = 2,而foo是一个Object类的实例,所以实际上是修改了Object这个类,给这个类增加了一个属性bar,值为2。
后来,我们又用Object类创建了一个zoo对象let zoo = {},zoo对象自然也有一个bar属性了。
那么,在一个应用中,如果攻击者控制并修改了一个对象的原型,那么将可以影响所有和这个对象来自同一个类、父祖类的对象。这种攻击方式就是原型链污染。
(这样相当于给类添加了新的属性,而这个对象中也有bar这个属性,所以类的改变并不会影响到已经创建好的对象的属性,但是新创建的对象就不一样了)
1.首先我们要知道 构造函数.prototype指向的是一个对象(原型)
2.任何对象都有一个原型对象,这个原型对象由对象的内置属性__proto__
指向它的构造函数的prototype指向的对象,即任何对象都是由一个构造函数创建的
3.只有构造函数内才有ptorotype属性
4.每个对象都内含有一个属性:__proto__
,也就是说就算对象里面没有对这个属性进行赋值,那么也是有这个属性的
5.原型链的核心就是依赖对象__proto__
的指向,当访问的属性在该对象不存在时,就会向上从该对象构造函数的prototype
的进行查找,直至查找到Object时,就没有指向了。如果最终查找失败会返回undefined
或报错
哪些情况下原型链会被污染?
在实际应用中,哪些情况下可能存在原型链能被攻击者修改的情况呢?
我们思考一下,哪些情况下我们可以设置__proto__
的值呢?其实找找能够控制数组(对象)的“键名”的操作即可:
(因为对象和属性之间的表达方式很多,与数组的表达方式是一样的)
对象merge 对象clone(其实内核就是将待操作的对象merge到一个空对象中) 以对象merge为例,我们想象一个简单的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]}}
}
这里如果我们想象一下,如果我们让key是__proto__
,的话那么是不是就可以对原型造成影响,最终影响到实例化出来的类呢?
我们用如下代码实验一下:
但是我们发现 这里的c并没有被污染
这是因为这里我们let o2 = {a: 1, "__proto__
": {b: 2}}中的proto已经作为的是o2的原型,也就是说这里o2[__proto__
]={b:2}(一个对象)并没有被解析成为键名,也就是说这里我们取到的键就只有a,b也就是o1[a]=1,o1[b]=2
所以我们就要使用JSON.parse
所以说我们代码改成这样:
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]}}
}
let o1 = {}
let o2 = JSON.parse('{"a": 1, "__proto__": {"b": 2}}')
merge(o1, o2)
console.log(o1.a, o1.b)//1,2o3 = {}
console.log(o3.b)//2
注意let o2 = {"a": 1, "proto": {"b": 2}}这种写法和let o2 = {a: 1, "__proto__
": {b: 2}}写法结果是一样的
分析 :
如果是第一种情况 (没有JSON.parse的情况),我们上面谈到,在进行键值赋值之前就会把proto解析掉,让其指向其构造函数的prototype指向的对象,也就是说原型o2的原型对象就成了{"b":2},就不是最上层的Object,就达不到污染的目的
这个时候我们看我们的o2对象 :
只有一个对象
我们查看它__proto__
指向的对象
那么我们继续往上面读取一下 :
发现才是最上层的Object.prototype对象,并且没有任何属性
我们知道我们给对象添加属性都是直接在后面加上.(点),然后在.(点)的后面加上属性的值
我们来看看o1:
这也证实了proto确实没有被当做键名来给a
如果我们这里能够达到这个效果:a.__proto__
.b=2的话不就污染到了嘛,因为a的上一层就是Object
所以我们看看加了JSON.parse的情况,这个会把我们的属性名当做键名来解析我们来分析一下流程 :
for (let key in source) {if (key in source && key in target) {merge(target[key], source[key])} else {target[key] = source[key]}}
let o1 = {}
let o2 = JSON.parse('{"a": 1, "__proto__": {"b": 2}}')
merge(o1, o2)
这里 首先加上了JSON.parse的时候这个时候__proto__
就会被当做键名了:
赋值过程中,首先会是o1[a]=o2[a]=1,
然后就回到proto这个键名,由于上面我们提及到只要是对象都会有一个__proto__
属性,所以这里会进入到if语句里面,也就是说两个对象都有__proto__
这个键名
接下来的赋值就是 :
target会变成o1[__proto__
],source会变成o2[__proto__
],接下来key就变成了b了,然后就会进行:
o1[__proto__
] [b]=o2[__proto__
] [b]=2(o2[__proto__
]就是{"b":2},然后[b]就相当于是.b
这里o1[__proto__
] [b]就相当于o1.__proto__
.b=2也相当于Object.prototype.b=2
这样就相当于给最顶层的Object.prototype所指向的对象添加了属性
所以我们随便创建一个对象也就有了b这个属性
这就是所谓的原型链污染的常见基础分析了
-------------------------------------------------------------------------------------------何秋莲 ❥(^_-) ❥(^_-)-----------------
js原型链污染(超详细)相关推荐
- JavaScript原型链污染攻击
前言 最近在看js的时候看到p神的一篇关于js原型链污染的文章,学习一下. 下面转自p神:深入理解 JavaScript Prototype 污染攻击 还有一篇案例关于js原型链污染的ctf题:从一道 ...
- Kibana未授权访问漏洞记录(CVE-2019-7609,Kibana的RCE,原型链污染,端口:5601)
Kibana介绍 Kibana是一个开源的分析与可视化平台,设计出来用于和Elasticsearch一起使用的.你可以用kibana搜索.查看存放在Elasticsearch中的数据.Kibana与E ...
- XSS注入进阶练习篇(三) XSS原型链污染
XSS原型链污染 1.原型链的概念 1.1 构造函数的缺点 1.2 prototype 属性的作用 1.3 原型链 1.4 `constructor`属性 1.5 `prototype`和`__pro ...
- Javascript Prototype污染攻击(原型链污染,Bugku-web-sodirty wp)
prototype 它表示原型,样本,标准. 在javascript中,你使用构造函数时会创建一些属性和方法. 在构造方法时,你书写了函数的内容,那么,当你每创建一次对象时就会执行一次函数内容并将方法 ...
- 详解JS原型链与继承
详解JS原型链与继承 JavaScript 目录 摘自JavaScript高级程序设计: 概念 确定原型和实例的关系 原型链的问题 借用构造函数 组合继承 原型继承 寄生式继承 寄生组合式继承 new ...
- nodejs原型链污染
参考两位大佬的博客: 深入理解 JavaScript Prototype 污染攻击 | 离别歌 (leavesongs.com) 继承与原型链 - JavaScript | MDN (mozilla. ...
- 简单粗暴地理解js原型链–js面向对象编程
简单粗暴地理解js原型链–js面向对象编程 作者:茄果 链接:http://www.cnblogs.com/qieguo/archive/2016/05/03/5451626.html 原型链理解起来 ...
- 从实现角度分析js原型链
从实现角度分析js原型链 欢迎来我的博客阅读:<从实现角度分析js原型链> 网上介绍原型链的优质文章已经有很多了,比如说: https://github.com/mqyqingfeng/B ...
- JS 原型链图形详解
JS原型链 这篇文章是「深入ECMA-262-3」系列的一个概览和摘要.每个部分都包含了对应章节的链接,所以你可以阅读它们以便对其有更深的理解. 对象 ECMAScript做为一个高度抽象的面向对象语 ...
最新文章
- 高效职场人不得不懂的“脑”知识
- 机器学习:基于关联规则的多标签分类器
- npm: 权限阻止修复
- python怎样实现封装_大牛教你如何封装 Python 代码,实现自动发送邮件只需三行代码...
- 为什么已有Elasticsearch,我们还要重造实时分析引擎AresDB?
- Chrome 浏览器扩展 - Dark Web - Dark Theme for Chrome
- h5应用 vue 钉钉_uniapp开发一个小视频应用(一)
- 页面加载完毕执行多个JS函数
- mybatis-plus -- mapper中foreach循环操作(新增,或修改)
- [礼仪大赛/模特比赛策划方案]现场场景描述
- android office转pdf插件,Office自带Word转PDF插件 让office的另存为可存储为PDF文件
- 2021-10-18
- 华为OSN1500B故障应急处理
- 软件测试肖sir___项目讲解之银行项目
- 解决Ubuntu16.04解压cudnn文件时报错could not create a hard link file
- js原生往父元素中添加子元素
- 电子邮件群发软件哪种好 电子邮件群发软件怎么用
- linux读和写线程同步,Linux:使用读写锁使线程同步
- 虚拟摄像头/无人直播效果分析,可替抖音/微信
- 平凡之路_2022年
热门文章
- Qml学习记录 一(风车动画详细建造步骤)
- 服务器 16路直连 英特尔,16核超猛神U:Intel Xeon D-1587性能测试
- 【Python】Pandas通过索引的方式去重df[~df.index.duplicated()]
- Linux深入探索04-Bash shell
- 线性代数Python计算:消元法与矩阵初等变换
- 【必读】关于赞赏的说明
- python极简教程_Python 极简教程(一)前言
- 设置显示实体的颜色 byLayer/byBlock
- Mongodb 之 $elemMatch 作用
- java rx.observable_Rxjava2 Observable的错误处理操作详解及实例