闭包漏洞问题

题目内容

如何在不改变下面代码的情况下,修改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]
    }
  }
})()
Last Updated: