在Node.js 6.0.0版本之前,创建缓冲区的方式是通过Buffer的构造函数来创建实例。以下是示例。
上述例子中,使用new关键字创建Buffer实例,它根据提供的参数返回不同的Buffer。其中,将数字作为第一个参数传递给Buffer()会分配一个指定大小的新Buffer对象。在Node.js 8.0.0之前,为此类Buffer实例分配的内存未初始化,并且可能包含敏感数据,因此随后必须使用buf.fill(0)或写入整个Buffer来初始化此类Buffer实例。
因此初始化缓存区其实有两种方式:创建快速但未初始化的缓冲区与创建速度更慢但更安全的缓冲区。但这两种方式并未在API上明显体现出来,因此可能会导致开发人员的误用,引发不必要的安全问题。所以,初始化缓冲区的安全API与非安全API之间需要有更明确的区分。
为了使Buffer实例的创建更可靠且更不容易出错,在新的Buffer()中,构造函数已被弃用,由单独的Buffer.from()、Buffer.alloc()和Buffer.allocUnsafe()方法来替代。
新的API包含以下几种。
·Buffer. from(array)返回一个新的Buffer,其中包含提供的八位字节的副本。
·Buffer. from(arrayBuffer[,byteOffset[,length]])返回一个新的Buffer,它与给定的ArrayBuffer共享相同的已分配内存。
·Buffer. from(buffer)返回一个新的Buffer,其中包含给定Buffer的内容副本。
·Buffer. from(string[,encoding])返回一个新的Buffer,其中包含提供的字符串的副本。
·Buffer. alloc(size[,fill[,encoding]])返回指定大小的新初始化Buffer。此方法比Buffer.allocUnsafe(size)慢,但保证新创建的Buffer实例永远不会包含可能敏感的旧数据。
·Buffer. allocUnsafe(size)和Buffer.allocUnsafeSlow(size)分别返回指定大小的新未初始化缓冲区。由于缓冲区未初始化,因此分配的内存段可能包含敏感的旧数据。如果size小于或等于Buffer.poolSize的一半,则Buffer.allocUnsafe()返回的缓冲区实例可以从共享内部内存池中分配。Buffer.allocUnsafeSlow()返回的实例从不使用共享内部内存池。
正如上面API所描述的,API在使用时要区分场景,毕竟不同的API对于数据的安全性有所差异。以下是Buffer的alloc方法和allocUnsafe方法使用的例子。
输出内容如下。
可以看到,allocUnsafe分配的缓存区中包含了旧数据,而且旧数据的值是不确定的。之所以会有这种旧数据产生,是因为调用Buffer.allocUnsafe()和Buffer.allocUnsafeSlow()时,分配的内存段未初始化(它不会被清零)。虽然这种设计使得内存分配非常快,但分配的内存段可能包含敏感的旧数据。使用由Buffer.allocUnsafe()创建的缓冲区而不完全覆盖内存可以允许在读取缓冲区内存时泄漏此旧数据。虽然使用Buffer.allocUnsafe()有明显的性能优势,但必须格外小心,以避免将安全漏洞引入应用程序。
如果想清理旧数据,可以使用fill()方法。示例如下。
通过填充零的方式(fill(0)),可以成功清理掉allocUnsafe分配的缓冲区中的旧数据。
注意安全和性能是天平的两端,要获取相对的安全,就要牺牲相对的性能。因此,开发人员在选择使用安全或非安全的方法时,一定要基于自己的业务场景来考虑。
本节例子可以在“buffer-demo/safe-and-unsafe.js”文件中找到。
可以使用——zero-fill-buffers命令行选项启动Node.js,这样所有新分配的Buffer实例在创建时默认为零填充,包括new Buffer(size)、Buffer.allocUnsafe()、Buffer.allocUnsafeSlow()和new SlowBuffer(size)。
以下是启用零填充的示例。
正如前文所述,使用零填充虽然可以获得数据上的安全,但一定是以牺牲性能为代价,因此使用此标志可能会对性能产生重大负面影响。建议仅在必要时使用——zero-fill-buffers选项。
当字符串数据存储在Buffer实例中或从Buffer实例中提取时,可以指定字符编码。
上述例子中,在初始化缓冲区数据时使用UTF-8,而后在提取缓冲区数据时,转为了十六进制字符和Base64编码。
Node. js当前支持的字符编码包括以下几种。
·ascii:仅适用于7位ASCII数据。此编码速度很快,如果设置则会剥离高位。
·utf8:多字节编码的Unicode字符。许多网页和其他文档格式都使用UTF-8。涉及中文字符时,建议采用该编码。
·utf16le:2个或4个字节,little-endian编码的Unicode字符。
·ucs2:utf16le的别名。
·base64:Base64编码。从字符串创建缓冲区时,此编码也将正确接受RFC 4648规范指定的“URL和文件名安全字母” 。
·latin1:将Buffer编码为单字节编码字符串的方法。
·binary:latin1的别名。
·hex:将每个字节编码为两个十六进制字符。
本节例子可以在“buffer-demo/character-encodings.js”文件中找到。