You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何仅读取文件片段检测RAR/ZIP压缩文件是否受密码保护?

检测ZIP/RAR文件是否加密(仅读取少量文件片段)

现有基于zipfile和rarfile库的testzip()/testrar()方法需要读取整个文件,不适合处理大型压缩文件。我们可以通过解析压缩文件的头部结构,仅读取前几KB数据来判断是否加密,无需加载完整文件。

实现思路

ZIP文件

ZIP文件的每个条目都包含局部文件头,其中的通用位标记(General Purpose Bit Flag)第0位为加密标识:如果该位为1,说明条目被加密。我们只需读取文件开头的局部文件头,检查这个标记即可。

RAR文件

RAR文件由多个块组成,文件头块中包含加密标记:

  • 旧版RAR(v4及以下):块头的Flags字段第3位表示加密
  • RAR 5.0:块头的Flags字段第2位表示加密

只需读取文件开头的块头信息,即可判断是否加密。

代码实现

import struct

def is_zip_encrypted_stream(file_obj):
    """从文件流中检测ZIP是否加密(仅读取前几KB)"""
    try:
        # 读取ZIP局部文件头的前30字节(足够获取通用位标记)
        header = file_obj.read(30)
        if len(header) < 30:
            return False
        
        # 解析通用位标记(偏移量6,2字节小端)
        general_purpose_flag = struct.unpack('<H', header[6:8])[0]
        # 第0位为1表示加密
        return (general_purpose_flag & 0x0001) != 0
    except Exception:
        return False

def is_rar_encrypted_stream(file_obj):
    """从文件流中检测RAR是否加密(仅读取前几KB)"""
    try:
        # 读取RAR文件头的前7字节(判断版本)
        header = file_obj.read(7)
        if len(header) < 7:
            return False
        
        # 检查RAR签名(RAR v4: 0x526172211A0700;RAR v5: 0x526172211A0701)
        signature = struct.unpack('<7B', header)
        if signature[:6] != (0x52, 0x61, 0x72, 0x21, 0x1A, 0x07):
            return False
        
        is_rar5 = (signature[6] == 0x01)
        if is_rar5:
            # RAR 5.0:读取块头的Flags字段(偏移量10,4字节小端)
            file_obj.seek(10)
            flags = struct.unpack('<I', file_obj.read(4))[0]
            # 第2位为1表示加密
            return (flags & 0x00000004) != 0
        else:
            # RAR v4:读取块头的Flags字段(偏移量8,2字节小端)
            file_obj.seek(8)
            flags = struct.unpack('<H', file_obj.read(2))[0]
            # 第3位为1表示加密
            return (flags & 0x0008) != 0
    except Exception:
        return False

# 示例用法(流式读取本地文件)
def check_encrypted_file(file_path):
    with open(file_path, 'rb') as f:
        # 先判断文件类型
        f.seek(0)
        magic = f.read(4)
        f.seek(0)
        if magic.startswith(b'PK\x03\x04'):
            return is_zip_encrypted_stream(f)
        elif magic.startswith(b'Rar!'):
            return is_rar_encrypted_stream(f)
        else:
            return False

# 测试
zip_path = "example.zip"
rar_path = "example.rar"
print(f"{zip_path} 是否加密: {check_encrypted_file(zip_path)}")
print(f"{rar_path} 是否加密: {check_encrypted_file(rar_path)}")

说明

  1. 流式兼容:该方法支持从任意可读取的字节流(如上传时的临时流、网络流)中读取,无需保存完整文件到磁盘
  2. 性能优势:仅读取前几KB数据,处理大型压缩文件时几乎无性能损耗
  3. 局限性:
    • ZIP文件:如果是分卷ZIP或第一个条目未加密但后续条目加密,会出现误判(但这类场景较少见)
    • RAR文件:仅检测主文件头的加密标记,部分特殊分卷或加密方式可能需要额外处理,但覆盖绝大多数常规场景

内容的提问来源于stack exchange,提问作者Mehrdad HMT

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.29 05:17:43