Buffer.from()、Buffer.alloc() 与 Buffer.allocUnsafe()
在 Node.js 6.0.0 之前的版本中,Buffer
实例是使用 Buffer
构造函数创建的,它根据提供的参数以不同的方式分配返回的 Buffer
:
- 将数字作为第一个参数传给
Buffer()
(例如new Buffer(10)
)会分配指定大小的新Buffer
对象。 在 Node.js 8.0.0 之前,为此类Buffer
实例分配的内存未初始化并且可能包含敏感数据。 此类Buffer
实例必须随后通过使用buf.fill(0)
或在从Buffer
读取数据之前写入整个Buffer
来初始化。 虽然此行为是为了提高性能,但开发经验表明,在创建快速但未初始化的Buffer
与创建较慢但更安全的Buffer
之间需要更明确的区别。 从 Node.js 8.0.0 开始,Buffer(num)
和new Buffer(num)
返回带有初始化内存的Buffer
。 - 传入字符串、数组或
Buffer
作为第一个参数会将传入的对象的数据复制到Buffer
。 - 传入
ArrayBuffer
或SharedArrayBuffer
返回Buffer
,它与给定的数组缓冲区共享分配的内存。
由于 new Buffer()
的行为因第一个参数的类型而异,因此当未执行参数验证或 Buffer
初始化时,可能会无意中将安全性和可靠性问题引入到应用程序中。
例如,如果攻击者可以使应用程序接收到预期为字符串的数字,则应用程序可能会调用 new Buffer(100)
而不是 new Buffer("100")
,从而导致它分配 100 字节的缓冲区,而不是分配内容为 "100"
的 3 字节缓冲区。
这通常可以使用 JSON API 调用实现。
由于 JSON 区分数字和字符串类型,因此它允许在未充分验证其输入的天真编写的应用程序可能期望始终接收字符串的情况下注入数字。
在 Node.js 8.0.0 之前,100 字节的缓冲区可能包含任意预先存在的内存数据,因此可用于向远程攻击者公开内存机密。
从 Node.js 8.0.0 开始,不会发生内存暴露,因为数据是零填充的。
但是,其他攻击仍然是可能的,例如导致服务器分配非常大的缓冲区,导致性能下降或因内存耗尽而崩溃。
为了使 Buffer
实例的创建更可靠且不易出错,new Buffer()
构造函数的各种形式已被弃用,并由单独的 Buffer.from()
、Buffer.alloc()
和 Buffer.allocUnsafe()
方法取代。
开发人员应将 new Buffer()
构造函数的所有现有用途迁移到这些新 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
实例永远不会包含可能敏感的旧数据。 如果size
不是数值,则会抛出TypeError
。Buffer.allocUnsafe(size)
和Buffer.allocUnsafeSlow(size)
分别返回指定size
的新的未初始化的Buffer
。 由于Buffer
未初始化,分配的内存段可能包含潜在敏感的旧数据。
如果 size
小于或等于 Buffer.poolSize
的一半,则 Buffer.allocUnsafe()
和 Buffer.from(array)
返回的 Buffer
实例可以从共享内部内存池中分配。
Buffer.allocUnsafeSlow()
返回的实例从不使用共享内部内存池。
In versions of Node.js prior to 6.0.0, Buffer
instances were created using the
Buffer
constructor function, which allocates the returned Buffer
differently based on what arguments are provided:
- Passing a number as the first argument to
Buffer()
(e.g.new Buffer(10)
) allocates a newBuffer
object of the specified size. Prior to Node.js 8.0.0, the memory allocated for suchBuffer
instances is not initialized and can contain sensitive data. SuchBuffer
instances must be subsequently initialized by using eitherbuf.fill(0)
or by writing to the entireBuffer
before reading data from theBuffer
. While this behavior is intentional to improve performance, development experience has demonstrated that a more explicit distinction is required between creating a fast-but-uninitializedBuffer
versus creating a slower-but-saferBuffer
. Since Node.js 8.0.0,Buffer(num)
andnew Buffer(num)
return aBuffer
with initialized memory. - Passing a string, array, or
Buffer
as the first argument copies the passed object's data into theBuffer
. - Passing an
ArrayBuffer
or aSharedArrayBuffer
returns aBuffer
that shares allocated memory with the given array buffer.
Because the behavior of new Buffer()
is different depending on the type of the
first argument, security and reliability issues can be inadvertently introduced
into applications when argument validation or Buffer
initialization is not
performed.
For example, if an attacker can cause an application to receive a number where
a string is expected, the application may call new Buffer(100)
instead of new Buffer("100")
, leading it to allocate a 100 byte buffer instead
of allocating a 3 byte buffer with content "100"
. This is commonly possible
using JSON API calls. Since JSON distinguishes between numeric and string types,
it allows injection of numbers where a naively written application that does not
validate its input sufficiently might expect to always receive a string.
Before Node.js 8.0.0, the 100 byte buffer might contain
arbitrary pre-existing in-memory data, so may be used to expose in-memory
secrets to a remote attacker. Since Node.js 8.0.0, exposure of memory cannot
occur because the data is zero-filled. However, other attacks are still
possible, such as causing very large buffers to be allocated by the server,
leading to performance degradation or crashing on memory exhaustion.
To make the creation of Buffer
instances more reliable and less error-prone,
the various forms of the new Buffer()
constructor have been deprecated
and replaced by separate Buffer.from()
, Buffer.alloc()
, and
Buffer.allocUnsafe()
methods.
Developers should migrate all existing uses of the new Buffer()
constructors
to one of these new APIs.
Buffer.from(array)
returns a newBuffer
that contains a copy of the provided octets.Buffer.from(arrayBuffer[, byteOffset[, length]])
returns a newBuffer
that shares the same allocated memory as the givenArrayBuffer
.Buffer.from(buffer)
returns a newBuffer
that contains a copy of the contents of the givenBuffer
.Buffer.from(string[, encoding])
returns a newBuffer
that contains a copy of the provided string.Buffer.alloc(size[, fill[, encoding]])
returns a new initializedBuffer
of the specified size. This method is slower thanBuffer.allocUnsafe(size)
but guarantees that newly createdBuffer
instances never contain old data that is potentially sensitive. ATypeError
will be thrown ifsize
is not a number.Buffer.allocUnsafe(size)
andBuffer.allocUnsafeSlow(size)
each return a new uninitializedBuffer
of the specifiedsize
. Because theBuffer
is uninitialized, the allocated segment of memory might contain old data that is potentially sensitive.
Buffer
instances returned by Buffer.allocUnsafe()
and
Buffer.from(array)
may be allocated off a shared internal memory pool
if size
is less than or equal to half Buffer.poolSize
. Instances
returned by Buffer.allocUnsafeSlow()
never use the shared internal
memory pool.