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++代码,往往正式运行逻辑之前会做一大堆的参数有效性检查。因为我们永远都不知道调用者(用户)会怎么来折磨我们设计的代码。把每一次调用,当作是一次恶意攻击。做好足够多的检查,并保证自己的代码不会因为恶意的参数输入,而崩溃或者逻辑出现异常。这是开发者必须有的基本技能。
领取专属 10元无门槛券
私享最新 技术干货