必须用 finfo_file() 校验真实 MIME 类型而非扩展名或 $_FILES['type'],再用 ffprobe 验证视频结构完整性,最后结合文件存在性、大小、上传状态检查,三层校验缺一不可。

用 finfo_file() 检查视频 MIME 类型是否匹配扩展名
仅靠文件后缀(如 .mp4)完全不可信,攻击者可把恶意文件重命名为 evil.jpg 上传。PHP 的 finfo_file() 能读取文件真实二进制头信息,判断是否为合法视频。
常见错误是直接用 $_FILES['video']['type'] —— 这个值由浏览器提供,极易伪造,必须丢弃。
-
finfo_open(FILEINFO_MIME_TYPE)返回类似video/mp4、video/quicktime的 MIME,需白名单校验 - 注意:某些 MOV 文件可能返回
video/quicktime,而 FFmpeg 常识别为video/mp4,别硬写死=== 'video/mp4' - 对 Web 兼容性要求高的场景,建议允许:
video/mp4、video/webm、video/quicktime(对应 MOV)、video/x-ms-wmv(极少但存在)
if (!isset($_FILES['video']) || $_FILES['video']['error'] !== UPLOAD_ERR_OK) {
die('上传失败');
}
$finfo = finfo_open(FILEINFO_MIME_TYPE);
$mimeType = finfo_file($finfo, $_FILES['video']['tmp_name']);
finfo_close($finfo);
$allowedTypes = ['video/mp4', 'video/webm', 'video/quicktime', 'video/x-ms-wmv'];
if (!in_array($mimeType, $allowedTypes)) {
die('不支持的视频格式');
}
用 get_headers() 或 file_exists() 验证临时文件未被篡改
上传完成后,PHP 把文件暂存到系统临时目录(如 /tmp/phpXXXXXX),但这个路径可能被其他进程或符号链接攻击干扰。必须确认该文件真实存在、可读、且大小合理。
容易忽略的是:上传超时或中断会导致 $_FILES['video']['tmp_name'] 指向一个空文件或已删除路径,直接调用 move_uploaded_file() 可能静默失败。
立即学习“PHP免费学习笔记(深入)”;
- 先用
is_uploaded_file($_FILES['video']['tmp_name'])确认是 PHP 本次上传生成的临时文件(防路径遍历) - 再用
filesize($_FILES['video']['tmp_name'])判断是否为 0 —— 视频文件几乎不可能为 0 字节 - 设置合理上限,例如
(500MB),避免 DoS 攻击;注意upload_max_filesize和post_max_sizephp.ini 配置需同步调整
用 ffprobe 检查视频元数据与流结构(真正校验完整性)
finfo_file() 只能看文件头,无法发现损坏的帧、断裂的索引(moov box 位置异常)、或伪视频(如 JPEG 数据拼接在 MP4 容器里)。要真正验证「能播放」,必须调用 FFmpeg 工具链。
关键点:不要用 exec('ffmpeg -i ...') 捕获输出,FFmpeg 错误信息常写入 STDERR,PHP 默认不捕获;且需防止 shell 注入。
- 用
escapeshellarg()包裹临时文件路径,例如:escapeshellarg($_FILES['video']['tmp_name']) - 执行
ffprobe -v quiet -show_entries format=duration:stream=codec_type,width,height -of default=nw=1,检查是否输出codec_type=video且有有效width/height - 若返回空或报错(如
No such file or directory、Invalid data found when processing input),说明文件损坏或非标准视频 - 注意性能:
ffprobe对大文件会读取部分数据,但比完整转码快得多;生产环境建议加超时(如timeout 10s ffprobe ...)
为什么不能只依赖 md5_file() 或 hash_file()
哈希值只能验证「文件内容未被修改」,不能验证「它是不是一个有效的视频」。一个被截断的 MP4、头部损坏的 AVI、甚至纯文本文件,只要内容固定,哈希就稳定不变。
更危险的是:如果业务逻辑把哈希作为「视频可用」的依据(比如前端上传后立即展示预览),用户可能上传一个哈希正确但根本无法解码的文件,导致播放器崩溃或服务端解析失败。
-
hash_file('sha256', $path)适合做去重或传输校验,不是格式校验 - 若需双重保障,应先通过
finfo+ffprobe验证有效性,再计算哈希存库用于后续比对 - 特别注意:某些云存储(如 AWS S3)上传分片后合并,可能产生合法哈希但非法结构的文件 —— 校验必须在合并后、入库前完成
真正难的不是调哪个函数,而是分清「文件存在」「文件可信」「文件可播」三层校验,漏掉任何一层,都可能让损坏视频进入你的 CDN 或数据库。











