首页 文章

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

提问于
浏览
3

我有一个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 回答

  • 4

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

    这可能也是为什么许多散列库函数以这种方式设计的,具有更新和完成步骤 .

    回答你的问题: No, the file content is not kept in a buffer, but is rather transformed into a fixed size internal state.

  • 4

    所有现代加密哈希函数都以可以逐步更新的方式创建 .

    为了允许增量更新,消息的输入数据首先以块的形式排列 . 这些块按顺序处理 . 为此,实现通常在内部缓冲输入,直到它具有完整块,然后使用所谓的压缩函数将该块与当前状态一起处理以产生新状态 . 初始状态通常仅由预定的常数值组成 . 在调用 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位的块将不会使您耗尽内存 . 在可以计算最终散列值之前,散列实现当然不需要缓冲整个消息 .


    笔记:

    • MD5和SHA-1被认为是不安全的w.r.t.抗冲击性,最好不再使用,特别是在验证内容时;

    • A "compression function"是一个特定的加密概念;它不是LSZIP或类似的东西;

    • 可能存在专门的理论哈希,它们以不同的方式执行计算 - 从理论上讲,不需要将输入消息分成块并按顺序对块进行操作 . 不用担心,这些不太可能出现在您正在使用的库中;

    • 类似地,实现可能决定一次缓冲更多的块,但幸运的是,这是非常罕见的 . 通常只有一个块用作缓冲区 - 在某些情况下,缓冲几个块的性能可能更高;

    • 由于效率原因,某些低级实现可能需要您自己提供块 .

相关问题