最可靠方法是用 finfo_file() 基于内容识别 MIME 类型,再检测文件头是否含 PHP 标记(如

用 finfo_file() 检测真实 MIME 类型最可靠
仅靠扩展名(如 .php)判断脚本文件极不可靠——攻击者可上传 shell.jpg.php 或伪造后缀绕过。PHP 的 finfo_file() 基于文件内容识别类型,能有效识别被伪装的 PHP 脚本。
实操建议:
- 确保
fileinfo扩展已启用(php -m | grep fileinfo) - 使用
FILEINFO_MIME_TYPE模式,避免返回完整 MIME 字符串干扰判断 - 对上传临时文件路径调用,而非原始文件名
- 典型 PHP 脚本的 MIME 类型是
text/x-php或application/x-httpd-php,但不同系统可能略有差异,建议同时检查是否含php字样
$finfo = finfo_open(FILEINFO_MIME_TYPE);
$mimeType = finfo_file($finfo, '/tmp/php_upload_abc123');
finfo_close($finfo);
if (stripos($mimeType, 'php') !== false) {
// 可疑 PHP 脚本
}
检查文件头是否含 PHP 开始标记
MIME 类型可能被刻意伪造,进一步验证文件内容开头是否含 PHP 标记(如 、、='),这是脚本执行的前提。
注意点:
立即学习“PHP免费学习笔记(深入)”;
- 用
file_get_contents($path, false, null, 0, 128)读取前 128 字节即可,避免大文件性能损耗 - 需考虑编码问题:BOM 头(如 UTF-8 BOM)可能出现在
前,应先用trim($content, "\xEF\xBB\xBF")去除 -
是短标签,依赖short_open_tag配置,生产环境通常关闭,但攻击者仍可能利用,建议一并检测
警惕 .phtml、.php5、.inc 等非标准扩展名
Web 服务器配置决定哪些扩展名会被解析为 PHP。Apache 默认解析 .php、.phtml;Nginx 则完全依赖 location ~ \.php$ 这类正则匹配。攻击者常利用冷门扩展名绕过白名单。
检测时应:
- 不只比对扩展名白名单,而要结合 Web 服务器实际配置逻辑模拟判断
- 常见可执行扩展包括:
.php、.phtml、.php3、.php4、.php5、.php7、.phar、.inc(若被包含在其他脚本中) - 注意大小写:
.PHP在 Windows 下等效,Linux 下可能不解析,但某些 Nginx 配置会忽略大小写
为什么 mime_content_type() 和 getimagesize() 不推荐
mime_content_type() 是 finfo 的旧别名,已废弃,行为不稳定;getimagesize() 仅适用于图像,对 PHP 文件返回 false,但无法区分「不是图」和「是恶意脚本」,容易误判。
更危险的是:部分函数(如 exif_imagetype())在遇到非图像文件时会触发警告甚至解析失败,导致异常暴露路径或中断流程。
真正安全的做法是组合使用:finfo_file() 初筛 + 内容头检测 + 扩展名上下文校验,三者缺一不可。
尤其要注意:即使文件看起来像图片(MIME 为 image/jpeg),只要开头含 ,就可能是嵌入了 WebShell 的图片马——这种混合型文件最容易被漏检。











