首页
学习
活动
专区
工具
TVP
发布
精选内容/技术社群/优惠产品,尽在小程序
立即前往

Safe-Buffer源代码解析

1.Node.js的缓冲区安全漏洞

Node.js 有一个buffer API,功能类似于C的内存管理API,提供alloc,fill,copy等一些基础功能。为了更快的分配buff,Node.js的Buffer API内部使用了一个缓存池。用户尝试alloc(100)时,就会直接从缓存池中返回一块内存给调用者。当用户不再使用这块缓存,就会重新放回缓存池。当有些应用需要极致的速度性能时,这是一个非常棒的支撑。

但是alloc的结果,是没有被初始化的。

这里明显存在着一个安全隐患。

假设存在这样一个调用时序。

A:

1. var buff = alloc(100)

2. buff="password"

3.//使用buff

4.丢弃和释放buff

B. 1.var buff = alloc(100)

2.console.log(buff) //输出password

A的敏感数据就这样被B拿走了。

未初始化,加上内部缓存池,两者结合之后就产生了安全漏洞。

比如下面的代码

function toHex(str){

return new Buffer(str).tostring(hex)

}

这是一个看似正常的操作,把一个字符串转成16进制的数据。但这里存在一个调用假设,代码的设计者,认为调用者传入的str是字符串。

如果调用者传入一个数字,一个Number,结果会如何?结果返回一块未初始化的内存数据。

很有可能,一些内部数据有可能暴露给调用者,而且是服务器内存中的数据。我们不知道这些数据是什么,有可能是私钥,用户数据或者密码。

Javascript的弱类型的语言,如果设计者不做足够多的类型检查,这类隐患是很多见的。动态语言开发者很少会去做类型检查,甚至可能完全忘记了。一般情况下,如果调用者传入一个非预期类型参数,大部分情况下可能是程序崩溃掉。但Node.js的Buffer则成了一个安全漏洞。

一个真实的例子是

//Take a JSON payload and convert it to hex

varserver=http.createServer(function(req,res) {

vardata=''

req.setEncoding('utf8')

req.on('data',function(chunk) {

data+=chunk

})

req.on('end',function() {

varbody=JSON.parse(data)

res.end(newBuffer(body.str).toString('hex'))

})

})

server.listen(8080)

客户端只要发送一个json数据

{

"str":1000

}

就会从服务器端获取到1000字节的无初始化的内存数据。

2。Safe-Buffer.js

Safe-Buffer.js就是为了避免以上的问题,而对buffer API做了简单的包装和类型检测。明确的把alloc 改成了Buffer.allocUnsafe(number)

allocUnsafe就是明确告诉调用者,这是一个不安全的缓存操作函数,返回的是未初始化的内存数据,需要用户自己初始化。

同时增加了Buffer.alloc(size),返回一个内存块,但已经自动被初始化为0的(清空数据)

SafeBuffer.alloc = function (size, fill, encoding) {

if (typeof size !== 'number') { //size必须是数字

throw new TypeError('Argument must be a number')

}

var buf = Buffer(size)

if (fill !== undefined) {

//字符串类型,就按字符串填充

if (typeof encoding === 'string') {

buf.fill(fill, encoding)

} else {

buf.fill(fill)

}

} else {

//默认操作,填充0

buf.fill(0)

}

return buf

}

SafeBuffer.allocUnsafe = function (size) {

if (typeof size !== 'number') {

throw new TypeError('Argument must be a number')

}

//调用Node.js Buffer,不做任何后续处理

return Buffer(size)

}

3.每一次调用都当作恶意调用

动态语言有很多的便利性,有些操作技巧,是非动态语言所不能达到的。但不同于强类型静态语言,在编译的时候会自动做类型检查,凡是类型不匹配的操作都会被找出来。但动态语言都是直接运行的,也都是弱类型,后绑定操作比较多。但作为开发者,类型检查依然是必须的,这是为了防止自己的代码被滥用的一个很好的解决方案。string参数就是只能输入string,不能是number,也不能是byte,或者function,或者object。在很多c++代码,往往正式运行逻辑之前会做一大堆的参数有效性检查。因为我们永远都不知道调用者(用户)会怎么来折磨我们设计的代码。把每一次调用,当作是一次恶意攻击。做好足够多的检查,并保证自己的代码不会因为恶意的参数输入,而崩溃或者逻辑出现异常。这是开发者必须有的基本技能。

  • 发表于:
  • 原文链接https://kuaibao.qq.com/s/20181128G0V8UA00?refer=cp_1026
  • 腾讯「腾讯云开发者社区」是腾讯内容开放平台帐号(企鹅号)传播渠道之一,根据《腾讯内容开放平台服务协议》转载发布内容。
  • 如有侵权,请联系 cloudcommunity@tencent.com 删除。

扫码

添加站长 进交流群

领取专属 10元无门槛券

私享最新 技术干货

扫码加入开发者社群
领券