下面这段代码可以实现:通过立即执行函数o返回对象中的get方法,通过参数key得到fn函数内部对象obj中的值。

var fn = function () {
  var obj = {
    a: 1,
    b: 2
  }
  return {
    get: function (key) {
      return obj[key]
    }
  }
}()
console.log(fn.get('b')); // 2

这是一个典型的闭包场景,这种做法可以做到屏蔽fn内部的obj,不可在外部直接访问obj,只能通过返回的get方法读取数据

这是很多第三方库的做法,但是通过漏洞,也可以做到直接修改fn函数内部obj的值。

上述写法中,get方法的权限过大,除了obj自身的属性a和b之外,还可以访问到它的原型成员

方式一:valueOf

尝试获取valueOf方法:

console.log(fn.get('valueOf'));

【JS】闭包的漏洞-LMLPHP

可以正常访问到valueOf方法,但是执行后发现报错:

console.log(fn.get('valueOf')());

【JS】闭包的漏洞-LMLPHP

这是由于valueOf语法为:obj.valueOf(),上述代码执行后等于直接调用valueOf(),所以该方法不适合上述场景。

但是如果get方法返回的不是一个值,而是函数调用的话,那么该方法就可以正常执行了。具体如下:

var fn = function () {
  var obj = {
    a: ()=>{return 1},
    b: ()=>{return 2}
  }
  return {
    get: function (key) {
      return obj[key]()
    }
  }
}()

let o = fn.get('valueOf')
console.log(o)  // 正常获取
console.log(fn.get('a'))  // 1

篡改obj数据

o.a = () => {return "abcdefg"}
o.b = () => {return "1234567"}
console.log(fn.get('a')) // 'abcdefg' 
console.log(fn.get('b'))// '1234567'

方式二:Object.defineProperty

当一个属性为访问器时,读取这个属性,就会变成函数调用

在原型上随意添加一个属性

Object.defineProperty(Object.prototype,'qwe',{
	get(){
		return this
	}
})

此时通过fn中的get函数读取原型上的属性qwe时,就会触发get函数,return的this就是fn中的obj。

【JS】闭包的漏洞-LMLPHP
后续可以对这个对象进行篡改

let o = fn.get('qwe')
o.a = "123"
o.b = "456"
console.log(fn.get('a')) // '123'
console.log(fn.get('b')) // '456'

如何处理这个漏洞

如果封装一些公共模块儿时,一定要注意处理这个漏洞,避免数据被污染或恶意篡改。

解决方法

一、检查参数

get的参数key,要添加检查必须为obj本身的属性,不能是原型上的。

此时需要使用一个Object的静态方法:Object.hasOwnProperty

var fn = function () {
  var obj = {
    a: 1,
    b: 2
  }
  return {
    get: function (key) {
      if(obj.hasOwnProperty(key)){
      	return obj[key]
      }
      return undefined
    }
  }
}()

再次尝试获取在原型上添加的qwe属性,发现无法获取。

fn.get('qwe')

【JS】闭包的漏洞-LMLPHP

二、将fn内的obj原型设为空

使用Object.setPrototypeOf(),将obj的原型设置为空。

var fn = function () {
  var obj = {
    a: 1,
    b: 2
  }
  Object.setPrototypeOf(obj,null)
  return {
    get: function (key) {
      return obj[key]
    }
  }
}()

尝试获取obj,由于原型为null,所以返回undefined。

fn.get('qwe')

【JS】闭包的漏洞-LMLPHP
更多Object的静态方法可以看另一篇文章:Object的静态方法

03-27 10:05