当前位置: 首页 > 后端技术 > Node.js

Node.js指南(迁移到安全缓冲区构造函数)

时间:2023-04-03 10:05:24 Node.js

迁移到安全缓冲区构造函数迁移到Buffer.from()/Buffer.alloc()API。概述本指南描述了如何迁移到安全的Buffer构造函数方法,迁移修复了以下弃用警告:Buffer()和newBuffer()构造函数因安全性和可用性问题而被弃用,请使用newBuffer.alloc()、Buffer.allocUnsafe()或Buffer代替。from()构造方法。变体1:放弃对Node.js≤4.4.x和5.0.0-5.9.x(推荐)的支持。变体2:使用polyfill。变体3:手动检测,有保障措施。使用grep查找有问题的代码,只需运行grep-nrE'[^a-zA-Z](Slow)?Buffer\s*\('--exclude-dirnode_modules。它会在你自己的代码中找到它一切这可能是不安全的(有一些不太常见的例外)。使用Node.js8查找有问题的代码位如果您使用的是Node.js≥8.0.0(推荐),Node.js会提供更多选项来帮助您查找相关代码片段:--trace-warnings将导致Node.js显示此警告的堆栈跟踪以及Node.js打印的其他警告。--trace-deprecation做同样的事情,但仅用于弃用警告。--pending-deprecation将显示更多类型的弃用警告,特别是它还会显示Buffer()弃用警告,即使在Node.js8上也是如此。您可以使用环境变量设置这些标志:$exportNODE_OPTIONS='--trace-warnings--pending-deprecation'$catexample.js'usestrict';constfoo=newBuffer('foo');$节点示例。js(node:7147)[DEP0005]DeprecationWarning:出于安全性和可用性方面的考虑,不建议使用Buffer()和newBuffer()构造函数。请改用新的Buffer.alloc()、Buffer.allocUnsafe()或Buffer.from()构造方法。在showFlaggedDeprecation(buffer.js:127:13)在newBuffer(buffer.js:148:3)在Object.(/path/to/example.js:2:13)[...更多堆栈跟踪lines...]使用linters查找有问题的代码位ESLint规则no-buffer-constructor或node/no-deprecated-api也可以找到对已弃用的Buffer()API的调用,这些规则包含在一些预设中。但是有一个缺点,当Buffer被覆盖时,它并不总是正确工作,例如使用polyfill,因此建议结合上面的其他方法使用它。变体1:放弃对Node.js≤4.4.x和5.0.0-5.9.x的支持这是目前推荐的解决方案,并且仅意味着最小的开销。Node.js5.x发布系列自2016年7月起不受支持,Node.js4.x发布系列于2018年4月达到生命周期结束(→计划)。这意味着即使出现安全问题,这些版本的Node.js将不会收到任何更新,因此应尽可能避免使用这些版本。在这种情况下,您要做的是将所有newBuffer()或Buffer()调用转换为使用Buffer.alloc()或Buffer.from(),如下所示:对于newBuffer(number),替换为Buffer.alloc(数字)。对于newBuffer(string)(或newBuffer(string,encoding)),将其替换为Buffer.from(string)(或Buffer.from(string,encoding))。对于所有其他参数组合(这些更罕见),也将newBuffer(...arguments)替换为Buffer.from(...arguments)。请注意,在当前的Node.js版本上,Buffer.alloc()也比newBuffer(size).fill(0)更快,这就是您需要确保零??填充的原因。建议启用ESLint规则no-buffer-constructor或node/no-deprecated-api以避免意外使用不安全的BufferAPI。还有一个JSCodeshiftcodemod,用于自动将Buffer构造函数迁移到Buffer.alloc()或Buffer.from(),请注意,它目前仅在参数为文字或使用两个参数调用构造函数时有效。如果您当前支持那些较旧的Node.js版本并且无法删除对它们的支持,或者如果您支持包的旧分支,请考虑在较旧的分支上使用变体2或变体3,因此使用那些旧分支也会收到修复。这样,您将消除因粗心使用BufferAPI而导致的潜在问题,并且您的用户在Node.js10上运行代码时不会观察到运行时弃用警告。变体2:使用polyfills共有三种不同的polyfills可用:safer-buffer是使用newBuffer()时抛出的整个BufferAPI的替代品。您将遵循与变体1完全相同的步骤,但在所有使用新BufferAPI的文件中填充constBuffer=require('safer-buffer').Buffer。不要使用旧的新Buffer()API,在任何添加上述行的文件中,使用旧的新Buffer()API都会抛出错误。buffer-from和/或buffer-alloc是BufferAPI各自部分的ponyfills,你只需要添加与你正在使用的API对应的包。您可以使用适当的名称导入所需的模块,例如constbufferFrom=require('buffer-from')然后使用它而不是调用newBuffer(),例如:newBuffer('test')变成bufferFrom('test')。使用这种方法的一个缺点是从它们迁移会稍微更改一些代码(因为您将使用不同名称的Buffer.from())。safe-buffer也是整个BufferAPI的替代品,但是使用newBuffer()仍然可以像以前一样工作。这种方法的缺点是它允许您在代码中使用旧的newBuffer()API,这是有问题的,因为它可能会导致您的代码出现问题,并且会从Node.js10开始发出运行时弃用警告(阅读更多在这里)。请注意,在任何一种情况下,您还必须手动删除对旧BufferAPI的所有调用,只是抛出安全缓冲区本身并不能解决问题,它只是为新API提供了一个polyfill,我已经看到人们犯了这个错误。建议启用ESLint规则no-buffer-constructor或node/no-deprecated-api。不要忘记在放弃对Node.js<4.5.0的支持后删除polyfill的使用。变体3-手动检测,带安全措施如果您只在几个地方(例如一个)创建Buffer实例,或者如果您有自己的包装器,这将很有用。Buffer(0)是创建空缓冲区的一种特殊情况,可以安全地替换为Buffer.concat([]),它在Node.js0.8.x之前返回相同的结果。在Buffer(notNumber)之前:constbuf=newBuffer(notNumber,encoding);之后:letbuf;if(Buffer.from&&Buffer.from!==Uint8Array.from){buf=Buffer.from(notNumber,encoding);}else{if(typeofnotNumber==='number'){thrownewError('The"size"argumentmustbenotoftypenumber.');}buf=newBuffer(notNumber,encoding);}编码是可选的。请注意,typeofnotNumber必须在newBuffer()之前(对于notNumber参数未硬编码的情况)并且不是由Buffer构造函数的弃用引起的-这就是Buffer构造函数被弃用的原因。缺乏此类检查的生态系统包会导致许多安全问题——从DoS到当未经处理的用户输入最终进入Buffer(arg)时将敏感信息从进程内存泄露给攻击者等问题。当notNumber参数被硬编码时(例如文字“abc”或[0,1,2]),可以省略typeof检查。还要注意,使用TypeScript并不能解决这个问题——当从JS使用TypeScript编写的库时,或者当用户输入结束时——它的行为就像纯JS,因为所有类型检查只是转换时间,并不存在于实际中TS编译的JS代码。Buffer(number)支持Node.js0.10.x(及以下):varbuf;if(Buffer.alloc){buf=Buffer.alloc(number);}else{buf=newBuffer(number);buf.fill(0);}否则(Node.js≥0.12.x):constbuf=Buffer.alloc?Buffer.alloc(number):newBuffer(number).fill(0);在Buffer.allocUnsafe()上使用Buffer。调用allocUnsafe()时要非常小心:如果没有充分的理由,请不要使用它。例如,您可能永远看不到小缓冲区的性能差异,事实上,使用Buffer.alloc()可能更快。如果您的代码不在热代码路径中-您也可能不会注意到差异。请记住,零填充可将潜在风险降至最低。如果您使用它,请确保永远不会以部分填充状态返回缓冲区。如果您按顺序编写-始终将其截断为实际写入的长度。处理使用Buffer.allocUnsafe()分配的缓冲区时出错会导致各种问题,包括代码的未定义行为,以及向远程攻击者泄露敏感数据(用户输入、密码、凭据)。请注意,这同样适用于没有零填充的newBuffer()用法,具体取决于Node.js版本(缺少类型检查也将DoS添加到潜在问题列表中)。常见问题Buffer构造函数有什么问题?Buffer构造函数可用于以多种不同方式创建缓冲区:newBuffer(42)创建一个42字节的缓冲区,在Node.js8之前,出于性能原因,此缓冲区包含任意内存,其中可能包括任何来自密码和加密密钥的源代码。newBuffer('abc')创建一个包含字符串'abc'的UTF-8编码版本的Buffer,第二个参数可以指定另一种编码:例如,你可以使用newBuffer(string,'base64')来转换Base64String被转换为它表示的原始字节序列。还有其他几种参数组合。这意味着在像varbuffer=newBuffer(foo);这样的代码中,如果不知道foo的类型,则无法确定结果缓冲区的确切内容。有时foo的值来自外部源,例如,此函数可以作为Web服务器上的服务公开,以将UTF-8字符串转换为其Base64格式:functionstringToBase64(req,res){//请求主体应该有`{string:'foobar'}`的格式。constrawBytes=newBuffer(req.body.string);constencoded=rawBytes.toString('base64');res.end({encoded});}请注意,此代码不验证req.body.string的类型:req.body.string应该是一个字符串,如果是这样,你就可以开始了。req.body.string由发送请求的客户端控制。如果req.body.string是数字50,那么rawBytes将是50个字节:在Node.js8之前,内容将是未初始化的。在Node.js8之后,内容将是50个字节,值为0。由于缺乏类型检查,攻击者可以故意发送一个数字作为请求的一部分,使用它他们可以:读取未初始化的内存,这将泄露密码、加密密钥和其他类型的敏感信息(信息泄露)。强制程序分配大量内存,例如,当指定500000000作为输入值时,每个请求将分配500MB内存,这可以用来完全耗尽程序可用的内存并使其崩溃,或者减慢速度该程序显着(拒绝服务)。这两种情况都被认为是现实世界网络服务器环境中的严重安全问题。使用Buffer.from(req.body.string)时,传递一个数字将始终抛出异常,从而提供始终可以由程序处理的受控行为。Buffer()构造函数已被弃用一段时间,这真的是个问题吗?对npm生态系统中代码的调查表明,Buffer()构造函数仍在广泛使用,这包括新代码,并且此类代码的整体使用实际上有所增加。Previous:DockerizingNode.jsWebApplicationsNext:阻塞和非阻塞概述