Javascript原型链参考文章:继承与原型链
关于原型链
文章内关于原型和原型链的知识写的非常详细,就不再总结整个过程了,以下为几个比较重要的点:
- 在javascript,每一个实例对象都有一个prototype属性,prototype 属性可以向对象添加属性和方法。
例子:
object.prototype.name=value
- 在javascript,每一个实例对象都有一个
__proto__
属性,这个实例属性指向对象的原型对象(即原型)。可以通过以下方式访问得到某一实例对象的原型对象:
objectname["__proto__"]
objectname.__proto__
objectname.constructor.prototype
- 不同对象所生成的原型链如下(部分):
var o = {a: 1};
// o对象直接继承了Object.prototype
// 原型链:
// o ---> Object.prototype ---> null
var a = ["yo", "whadup", "?"];
// 数组都继承于 Array.prototype
// 原型链:
// a ---> Array.prototype ---> Object.prototype ---> null
function f(){
return 2;
}
// 函数都继承于 Function.prototype
// 原型链:
// f ---> Function.prototype ---> Object.prototype ---> null
原型链污染原理
对于语句:object[a][b] = value
如果可以控制a、b、value的值,将a设置为__proto__
,我们就可以给object对象的原型设置一个b属性,值为value。这样所有继承object对象原型的实例对象在本身不拥有b属性的情况下,都会拥有b属性,且值为value。
来看一个简单的例子:
object1 = {"a":1, "b":2};
object1.__proto__.foo = "Hello World";
console.log(object1.foo);
object2 = {"c":1, "d":2};
console.log(object2.foo);
最终会输出两个Hello World。为什么object2在没有设置foo属性的情况下,也会输出Hello World呢?就是因为在第二条语句中,我们对object1的原型对象设置了一个foo属性,而object2和object1一样,都是继承了Object.prototype。在获取object2.foo时,由于object2本身不存在foo属性,就会往父类Object.prototype中去寻找。这就造成了一个原型链污染,所以原型链污染简单来说就是如果能够控制并修改一个对象的原型,就可以影响到所有和这个对象同一个原型的对象。
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]
}
}
}
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)
需要注意的点是:
在JSON解析的情况下,__proto__
会被认为是一个真正的“键名”,而不代表“原型”,所以在遍历object2的时候会存在这个键。
最终输出的结果为:
1 2
2
可见object3的b是从原型中获取到的,说明Object已经被污染了。
在平时的挖洞过程中只需要在URL后面加上__proto__[xxx]=xxx
参数一般就能够检测出网站是否存在这个漏洞了。比如说https://test.com?__proto__[xxx]=yyy
如果说网站存在原型污染漏洞,那么在浏览器console中输入,document.xxx应该会返回值yyy。