zip 文件如何检测正确的密码?

信息安全 加密 密码 哈希 密码破解 压缩
2021-08-18 12:53:29

从加密的 zip 存档中提取文件时,会要求用户输入密码才能读取原始文件。

加密的 ZIP 如何检测用户何时提供了正确的密码?

显然它没有与某些后端服务连接,并且它不包含要比较的实际密码。那么它究竟是如何检查的呢?存档中是否包含原始密码的一些哈希?很容易找到这个哈希吗?

2个回答

循环冗余校验 (CRC) 字段用于确定文件是否被正确解密。引自原始ZIP 格式规范

标头解密后,Buffer 中的最后 1 或 2 个字节应该是被解密文件的 CRC 的高位字/字节,以 Intel 低字节/高字节顺序存储。2.0 之前的 PKZIP 版本使用 2 字节 CRC 校验;2.0 之后的版本使用 1 字节 CRC 校验。这可以用来测试提供的密码是否正确。

更新:从Info-ZIP的unzip源码可以看出,CRC值是用来检查密码是否正确的:

https://github.com/LuaDist/unzip/blob/master/crypt.c#L617

#ifdef ZIP10 /* check two bytes */
    c = hh[RAND_HEAD_LEN-2], b = hh[RAND_HEAD_LEN-1];
    Trace((stdout,
      "  (c | (b<<8)) = %04x  (crc >> 16) = %04x  lrec.time = %04x\n",
      (ush)(c | (b<<8)), (ush)(GLOBAL(lrec.crc32) >> 16),
      ((ush)GLOBAL(lrec.last_mod_dos_datetime) & 0xffff))));
    if ((ush)(c | (b<<8)) != (GLOBAL(pInfo->ExtLocHdr) ?
                           ((ush)GLOBAL(lrec.last_mod_dos_datetime) & 0xffff) :
                           (ush)(GLOBAL(lrec.crc32) >> 16)))
        return -1;  /* bad */
#else
    b = hh[RAND_HEAD_LEN-1];
    Trace((stdout, "  b = %02x  (crc >> 24) = %02x  (lrec.time >> 8) = %02x\n",
      b, (ush)(GLOBAL(lrec.crc32) >> 24),
      ((ush)GLOBAL(lrec.last_mod_dos_datetime) >> 8) & 0xff));
    if (b != (GLOBAL(pInfo->ExtLocHdr) ?
        ((ush)GLOBAL(lrec.last_mod_dos_datetime) >> 8) & 0xff :
        (ush)(GLOBAL(lrec.crc32) >> 24)))
        return -1;  /* bad */
#endif
    /* password OK:  decrypt current buffer contents before leaving */
    for (n = (long)GLOBAL(incnt) > GLOBAL(csize) ?
             (int)GLOBAL(csize) : GLOBAL(incnt),
         p = GLOBAL(inptr); n--; p++)
        zdecode(*p);
    return 0;       /* OK */

加密的 ZIP 如何检测用户何时提供了正确的密码?

显然它没有与某些后端服务连接,并且它不包含要比较的实际密码。那么它究竟是如何检查的呢?

简短的回答:大多数 zip 提取程序可能不检查密码。他们只是尝试解密数据,并可能检查它是否看起来像真实数据。Selcuk 的回答表明,一些程序可能会使用文件中包含的 CRC 来获得一定程度的数据正确解密的信心,但是从文本中的“应该”和“可以使用”来看,这听起来像是可选的(尽管鼓励) 用于使用 zip 格式的应用程序。

顺便说一下,这也可能允许在文件损坏的情况下进行(部分)数据恢复,因为应用程序可以简单地忽略 CRC 检查并从加密的 zip 中提取它可以从加密的 zip 中提取的内容,依靠用户找到坏文件(这可能与错误的密码取决于规范的其余部分是如何编写的)。