php文件上传白名单过滤必须在move_uploaded_file前完成,先用pathinfo提取小写扩展名并校验白名单,再用finfo_file验证真实mime类型,最后严格控制存储路径权限。

PHP文件上传白名单过滤的核心逻辑
白名单过滤必须在 $_FILES 临时文件移动前完成,且仅靠扩展名判断远远不够——攻击者可伪造 name 字段绕过。真正安全的做法是:先校验扩展名(白名单),再用 finfo_file() 检查实际 MIME 类型和内容。
定义扩展名白名单并用 in_array 判断的正确写法
扩展名需统一转为小写后比对,否则 .JPG 或 .Pdf 会被误拒。白名单数组应显式声明,避免从配置文件动态读取时引入不可控字符。
$allowed_exts = ['jpg', 'jpeg', 'png', 'gif', 'pdf'];- 从
$_FILES['file']['name']提取扩展名:用pathinfo($filename, PATHINFO_EXTENSION),不要用substr(strrchr($filename, '.'), 1)(会漏掉无扩展名或带多个点的文件) - 判断前必须
strtolower():in_array(strtolower($ext), $allowed_exts) - 注意:空扩展名(如
abc.)或只有点(.)的情况,pathinfo返回空字符串,需额外检查!empty($ext)
仅靠 in_array 扩展名判断为什么不够安全
攻击者上传 shell.php.jpg 并把 Web 服务器配置成解析 .jpg 为 PHP,或者利用 Apache 的多后缀解析漏洞(如 xxx.jpg.php),就能绕过扩展名检查。更危险的是直接改 POST 中的 name 字段为 webshell.php,但服务端仍收到一个看似合法的临时文件。
- 必须配合
finfo_open(FILEINFO_MIME_TYPE)获取真实类型,例如image/jpeg、application/pdf - 对图片类文件,建议额外调用
getimagesize()验证是否为有效图像(能防御部分 GIF 注入 PHP 代码的案例) - 不要信任
$_FILES['file']['type']—— 这个字段由浏览器提供,完全可伪造
完整校验流程中容易踩的坑
很多开发者把白名单判断放在 move_uploaded_file() 之后,导致恶意文件已落地;或忽略临时文件的 error 状态,让上传失败的文件被误处理。
- 第一步永远是检查
$_FILES['file']['error'] === UPLOAD_ERR_OK,否则后续全无意义 - 扩展名和 MIME 校验都通过后,再生成唯一文件名(如
uniqid() . '.' . $safe_ext),禁止直接使用原始name - 保存路径不能是 Web 可访问目录,或至少禁用该目录下脚本执行(如 Nginx 中配置
location ~ \.php$ { deny all; }) - 如果业务允许 PDF 上传,注意某些 PDF 可嵌入 JavaScript,需结合 PDF 解析库做进一步内容扫描(这不是白名单能覆盖的层面)
in_array() 而不碰 finfo 和权限控制,等于锁了门却把钥匙留在门外。php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











