PHP文件类型校验需先用正则提取并初筛扩展名(如/.(jpg|png|gif)$/i),再通过finfo获取真实MIME类型比对白名单,严禁依赖$_FILES['type']或单纯正则过滤。

PHP 文件类型校验不能只靠正则匹配扩展名,但扩展名初筛是常见且实用的第一步。关键在于:正则能快速过滤明显不符的文件名,但必须配合 mime_content_type() 或 finfo 做真实内容检测,否则极易被伪造。
提取并校验文件扩展名
仅靠文件名后缀判断类型风险高,但作为前置校验仍有必要。重点是写对、防路径干扰、区分大小写需求:
- 用
/.[a-zA-Z0-9]{2,8}$/提取最后一个点后的部分,避免误匹配路径中的点(如/var/log/error.log) - 若需严格限定常见图片格式,可用
/.(jpg|jpeg|png|gif|webp)$/i—— i 修饰符让 jpg/JPG 都通过 - 若业务要求扩展名全小写再比对,建议先
strtolower(pathinfo($filename, PATHINFO_EXTENSION)),再用简单字符串比较,比正则更稳
匹配带路径的完整文件名(含安全防护)
上传表单中用户可能提交形如 ../../etc/passwd.jpg 的恶意路径。正则可辅助剥离危险片段:
- 用
/^[^/\\]{1,255}.(?:jpg|png|pdf|docx?)$/u限制文件名不含斜杠、反斜杠,且长度合理 -
^和$必须存在,防止xxx.php.jpg绕过(实际执行的是 php) - 不推荐用正则“过滤”危险字符,而应直接拒绝含
../、%2e%2e%2f、空字节等的原始文件名
结合 MIME 类型做最终判定
正则只是名字层面的检查,真实类型必须读文件头:
立即学习“PHP免费学习笔记(深入)”;
- 使用
finfo_open(FILEINFO_MIME_TYPE)获取真实 MIME,例如image/jpeg、application/pdf - 将 MIME 与白名单比对:
in_array($mime, ['image/jpeg', 'image/png', 'application/pdf'], true) - 注意:
$_FILES['file']['type']可被客户端随意伪造,绝对不可信
常见错误与规避方式
这些坑在生产环境高频出现:
- 写
/.jpg$/却没加i修饰符 →photo.JPG被误拒;应写/.jpg$/i或统一转小写 - 用
preg_match('/.php$/i', $filename)检查是否为 PHP →shell.php.jpg会意外通过;应确保扩展名是**唯一且结尾**的,即/.php$/i+ 锚定$,同时检查是否含多个点 - 中文文件名如
报告.pdf在旧环境可能乱码 → 确保脚本文件存为 UTF-8 无 BOM,并在正则中加 u 修饰符(如/.pdf$/iu)



















