Discuz图片类型校验依赖服务端魔数检测+后缀白名单+getimagesize()三重验证,不信任前端accept或file.type;核心逻辑在class_upload.php的check_file_type()中,依次读取文件头16字节比对魔数、校验name后缀是否在后台上传设置白名单内、调用getimagesize()确认图像有效性。

Discuz 上传图片时如何校验文件类型
Discuz 对图片类型的校验是分层进行的,**前端仅做提示性过滤,后端才是真正的强制校验点**。它不依赖 accept 属性或 file.type,而是以服务端读取文件头(魔数)+ 后缀白名单 + MIME 映射三者结合的方式落地,避免绕过前端直接发包上传恶意文件。
核心校验逻辑在 source/class/upload/class_upload.php
Discuz 的上传主流程由该类统一接管,图片类型判断关键在 check_file_type() 方法中,具体行为如下:
- 先通过
fopen($file, 'rb')读取文件前 16 字节,比对预设的图片魔数(如JPEG对应\xFF\xD8\xFF,PNG对应\x89PNG\r\n\x1a\n) - 再检查
$_FILES['file']['name']的后缀是否在后台「上传设置」中允许的列表内(默认为jpg|jpeg|gif|png|bmp|webp) - 最后调用
getimagesize()尝试解析——若返回false,说明不是有效图像,直接拒绝 - 整个过程不信任
$_FILES['file']['type'],该值由浏览器提供,可被轻易伪造
后台「上传设置」决定白名单范围
路径:后台 → 全局 → 上传设置 → 「允许上传的文件类型」
- 该字段控制的是后缀白名单,格式为竖线分隔的正则片段(如
jpg|jpeg|png),不是 MIME 类型列表 - 修改后需清空
data/sysdata/cache_*,否则缓存可能使新规则不生效 - 即使此处加了
svg,Discuz 默认仍会因安全策略拒绝 SVG(无内置 SVG 魔数校验且getimagesize()不支持),需额外补丁
为什么不能只靠前端 accept="image/*" 或 file.type
Discuz 完全不依赖这些前端信号,原因很实际:
-
accept在 IE9、部分安卓 WebView 中被忽略;用户可手动切到“所有文件”并选任意后缀 -
file.type在文件无扩展名、或后缀被篡改(如shell.jpg.php)时不可靠,甚至返回空字符串 - 攻击者可构造合法图片头 + 嵌入 PHP 代码的文件(如 GIF89a),Discuz 的
getimagesize()能识别结构但无法阻止代码执行——所以它还会在保存后重命名(如转为123456789.jpg)并禁用执行权限
真正起作用的永远是服务端魔数 + 白名单 + 图像解析三重验证,缺一不可。漏掉任一环,都可能让伪装图片绕过检测。


















