闭包漏洞问题
题目内容
如何在不改变下面代码的情况下,修改obj对象
var o = (function () {
var obj = {
a: 1,
b: 2
}
return {
get: function(key) {
return obj[key]
}
}
})()
解析
上面的代码是一个典型的闭包,通过暴漏出的APIget函数来对其内部保护对象obj提供有限的访问。也就是通过get可以访问到其内部变量obj中的属性,但是访问不到obj对象本身。就像下面示例代码这样:
o.get('a') // 1
o.get('b') // 2
// 通过get访问不到obb本身
题目的问题是怎么在不改变上面代码的情况下修改其内部obj对象。
valueOf
比较容易想到的是,我们除了可以通过get访问obj本身的属性,也可以访问其原型链上的属性,而Object.prototype.valueOf()返回的就是对象本身,那通过o.get('valueOf')()不就可以拿到obj了吗?
o.get('valueOf')() // Uncaught TypeError: Cannot convert undefined or null to object
可以看到没那么简单,我们忽略了this指向问题。上面的代码等价于下面的代码,相当于函数直接调用,在非严格模式下this指向全局对象,而在严格模式下this指向undefined。
const valueOf = o.get('valueOf')
valueOf() // 函数直接调用
访问器属性和Object.defineProperty
那我们要怎么绕开this指向改变的问题呢?可以用访问器属性和Object.defineProperty
Object.defineProperty(Object.prototype, 'foo', {
get(){
return this
}
})
o.get('foo') // {a: 1, b: 2} 拿到了!
o.get('foo').a = 2 // 尝试修改属性
o.get('a') // 2 确实变了
o.get('foo').c = 3 // 加一个属性试试
o.get('c') // 3 新属性也加上了
解决方案
- 方案1:检查是否是
obj自身的属性
var o = (function () {
var obj = {
a: 1,
b: 2
}
return {
get: function(key) {
if (!Object.hasOwnProperty(key)) return
return obj[key]
}
}
})()
- 方案2:prototype置为null
var o = (function () {
var obj = {
a: 1,
b: 2
}
return {
get: function(key) {
Object.setPrototypeOf(obj, null)
return obj[key]
}
}
})()