应使用 python-magic 而非手动魔数匹配,因其底层调用 libmagic,支持 500+ 格式、嵌套结构、加密与变体文件,避免漏判误判及维护难题。

直接用 python-magic,别自己手写魔数匹配表——它底层调用系统 libmagic,覆盖 500+ 格式,能处理嵌套签名(比如 ZIP 内的 DOCX)、加密 PDF、带注释的 JPEG,而手动比对前 4–8 字节会漏判、误判、维护爆炸。
为什么不能只读前几个字节做 startswith() 匹配
魔数不是固定长度,也不是总在开头:PDF 真实签名是 %PDF-,但有些变体开头有 BOM 或空格;ZIP 文件(包括 DOCX/XLSX/PPTX)以 PK\x03\x04 开头,可后续还有加密标识;某些 HEIC 文件魔数分散在多个 offset;更麻烦的是,多个格式共享同一段头部(如某些加密 PDF 被 filetype 误标为 application/zip),纯字节匹配无法深入解析。
常见踩坑点:
- 只读 4 字节就返回,错过
\x89PNG\r\n\x1a\n(共 8 字节)导致 PNG 判为未知 - 用
open(...).read()(文本模式)而非open(..., 'rb'),导致字节解码失败或截断 - 没处理空文件或权限拒绝,程序直接抛
PermissionError或ValueError - 把
'GIF87a'和'GIF89a'当成两个独立 key,其实只需匹配b'GIF'前缀即可覆盖
python-magic 安装与平台适配要点
不装对依赖,import magic 会直接报 OSError: failed to find libmagic。不同系统路径和链接方式差异大,不能统一 pip install。
按平台操作:
- Windows:必须用
pip install python-magic-bin,它自带预编译的 DLL,无需额外配置环境变量 - macOS:先
brew install libmagic,再pip install python-magic;若用 M1/M2,确认 brew 安装的是 arm64 版本 - Linux(Ubuntu/Debian):
sudo apt-get install libmagic1 libmagic-dev,再pip install python-magic - Docker 镜像:Dockerfile 中加
RUN apt-get update && apt-get install -y libmagic1,否则容器内必挂
from_file() vs from_buffer() 怎么选
核心区别是数据源:磁盘路径 or 内存 bytes。选错会导致类型错乱、性能下降甚至崩溃。
使用建议:
- 传路径给
from_file():只接受本地绝对或相对路径,不支持 URL、io.BytesIO、pathlib.Path对象(除非已转为字符串) - 传 bytes 给
from_buffer():必须是bytes类型,不是str;常见错误是open(f).read()(Python 3 返回 str),应改为open(f, 'rb').read() - 小文件(from_buffer(),避免重复 IO
- 大文件或不确定是否加载完成:用
from_file(),它内部按需读取头部,不加载全文 - 文件被 Excel/Word 锁住时:
from_file()报PermissionError,此时只能走 buffer 流程(前提是你已有内容)
生产环境必须加的防护层
即使 python-magic 返回 image/jpeg,也不能直接信任——攻击者可以把 PHP 代码塞进 JPEG 的 EXIF 注释区,后缀改 .jpg,MIME 检测仍通过,但 Web 服务器若配置不当可能执行它。
安全校验必须三重交叉:
- 扩展名是否在白名单内(如
.jpg,.png) -
magic.Magic(mime=True).from_file()返回 MIME 是否匹配该扩展名(如.jpg→image/jpeg) - 必要时加轻量内容验证(如用
PIL.Image.open()尝试 decode,捕获IOError)
单独依赖任何一层都不可靠;魔数识别只是起点,不是终点。
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!











