nodejs crypto模块,hash.update()将所有输入存储在内存中

时间:2015-02-27 20:02:02

标签: node.js hash stream cryptography md5

我有一条API路由代理从浏览器/客户端到AWS S3的文件上传。

此API路由尝试在文件上传时对其进行流式传输,以避免在服务器上缓冲文件的整个内容。

但是,路由还会尝试计算文件正文的MD5校验和。由于文件的每个部分都是分块的,因此使用块调用hash.update()方法。

http://nodejs.org/api/crypto.html#crypto_hash_update_data_input_encoding

var crypto = require('crypto');
var hash = crypto.createHash('md5');
function write (chunk) {
  // invoked many times as file is uploaded
  hash.update(chunk);
}
function done() {
  // will hash buffer all chunks in memory at this point?
  hash.digest('hex');
}

Hash的实例是否会缓冲文件的所有内容以执行哈希计算(从而违背了避免在内存中缓冲整个文件内容的目标)?或者可以递增计算MD5哈希,而无需使用整个输入来执行计算?

2 个答案:

答案 0 :(得分:4)

MD5和其他一些哈希函数基于Merkle–Damgård construction。它支持数据的增量/渐进/流式散列。在将数据转换为内部状态(具有固​​定大小)之后,执行最后的完成步骤以通过填充和处理最后一个块来生成最终散列,然后通过简单地返回最终状态来生成。

这也可能是为什么许多散列库函数都是以更新和最终化步骤的方式设计的。

回答你的问题:不,文件内容不会保存在缓冲区中,而是转换为固定大小的内部状态。

答案 1 :(得分:4)

所有现代加密哈希函数的创建方式都可以对其进行增量更新。

为了允许增量更新,首先将消息的输入数据按块排列。这些块按顺序处理。为此,实现通常在内部缓冲输入,直到它具有完整的块,然后使用所谓的压缩函数 将其与当前状态一起处理以产生新状态。 / em>。初始状态通常仅由预定的常数值组成。在调用digest的过程中,最后一个块被填充-通常使用位填充和已处理字节数的编码-并计算最终状态;这可能需要一个没有任何消息数据的附加块。可以执行最后的操作,最后返回得到的哈希值。

对于MD5,使用Merkle–Damgård construction。此通用结构也用于SHA-1和SHA-2。 SHA-2是基于SHA-256(SHA-224)和SHA-512(SHA-384,SHA-512 / 224和SHA-512 / 256)算法的哈希系列。 MD5特别使用512位的块大小和128位的内部状态。简单地直接输出最后一个块的内部状态(包括填充),而无需对MD5,SHA-1,SHA-256和SHA-512进行任何后处理。

Keccak被选为SHA-3。它是基于海绵的构造,具有特定的压缩功能。这不是Merkle–Damgård哈希-这是为什么被选择为SHA-3的重要原因。它仍然具有Merkle–Damgård散列的所有更新属性,并且已经过设计以便与SHA-2兼容。像前面提到的哈希一样,它可以拆分并缓冲块,但是它具有更大的内部状态并对输出执行最终操作,因此可以说更加安全。

因此,当您使用现代的哈希结构(例如MD5)时,您在不知不觉中执行了额外的缓冲。幸运的是,为状态大小缓冲512位+ 128位的单个块不太可能使您用尽内存。哈希实现当然不需要在可以计算最终哈希值之前就缓冲整个消息。


注意:

    li.MD5和SHA-1被认为不安全耐冲击性,最好不再使用它们,尤其是在验证内容时
  • “压缩功能”是一种特定的加密概念;它不是 LSZIP或类似的内容;
  • 可能存在专门的理论哈希值,它们以不同的方式执行值的计算-从理论上讲,不需要将输入消息分为多个块并按顺序对这些块进行操作。不用担心,那些不太可能出现在您正在使用的库中;
  • 类似地,实现可能决定一次缓冲更多的块,但是幸运的是,这种情况也很少见。通常只使用一个块作为缓冲区-在某些情况下,代替几个块会更有效;
  • 出于效率考虑,某些低级别的实现可能会要求您自己提供模块。