视频上传必须用finfo检测真实MIME、FFmpeg验证结构、异步云审核并签名回调;禁用$_FILES['type']和扩展名校验,截图需场景变化帧+关键帧+getimagesize校验,命令执行须escapeshellarg防护。

视频上传环节是PHP应用最常被攻破的入口之一,单纯校验扩展名或 $_FILES['type'] 等于开门揖盗;真正的风控必须在文件落地瞬间启动异步审核,并彻底隔离执行风险——否则你防的不是攻击者,是自己的侥幸心理。
为什么不能信 $_FILES['video']['type'] 和文件扩展名
攻击者用十六进制编辑器改几个字节,就能让一个 PHP Webshell 假装成 video/mp4;$_FILES['type'] 完全由浏览器提交,.mov 后缀也能伪造为 video/quicktime。真实 MIME 必须用二进制头探测。
- 必须调用
finfo_open(FILEINFO_MIME_TYPE)+finfo_file()实际读取前 256 字节判断 - 拒绝
application/x-php、text/plain、image/svg+xml等可执行或含脚本类型的“伪装视频” - 即使
finfo返回video/mp4,也要立刻用 FFmpeg 探针验证结构合法性——执行ffmpeg -v error -i '.escapeshellarg($tmpPath).' -f null - 2>&1,返回码非 0 就是恶意 payload
如何安全触发云审核并避免 exec 注入
审核任务必须异步,但调用 exec()、shell_exec() 时若拼接用户可控路径,就是给 RCE 开后门。
- 所有传给 FFmpeg 或外部命令的路径必须用
escapeshellarg()包裹,且禁止任何用户输入参与构造命令字符串 - 审核任务 ID 应由服务端生成(如
bin2hex(random_bytes(16))),而非依赖$_FILES['name']或表单字段 - 把文件路径、用户 ID、时间戳写入 Redis 队列,由独立 worker 进程消费——worker 不接收 HTTP 请求,不解析用户输入,天然免疫注入
- 切勿在上传响应中同步调用云 API:超时、网络抖动、限流都会导致用户卡死,且暴露 access key 风险
截帧策略为何不能只写 fps=1
固定帧率截图会系统性漏掉黑屏、静帧、闪白、场景切换等高危内容,审核平台返回 “通过” 不代表真的安全。
立即学习“PHP免费学习笔记(深入)”;
- 优先用
-vf "select='gt(scene,0.4)'"提取场景变化帧,0.4 是实测平衡点:低于 0.3 易出大量重复帧,高于 0.5 会跳过快速转场 - 对短视频额外加一路
-skip_frame nokey强制提取关键帧,避免 I 帧缺失导致花屏漏检 - 每张截图生成后必须用
getimagesize()校验,排除空文件、损坏 JPEG、无像素数据等无效图——否则调用审核 API 会直接返回InvalidImage错误 - 截图命名禁用原始文件名,统一用哈希前缀(如
sha256_filepart_001.jpg),防止路径遍历或覆盖
回调地址签名验证为什么不能省
第三方审核服务(阿里云、腾讯云等)全部走异步 HTTP 回调,没签名验证 = 允许任何人伪造“审核通过”状态。
你得确保两件事同时成立:回调 URL 公网可达(NAT/内网部署必挂),且每次回调请求都带 HMAC-SHA256 签名并由 PHP 严格比对——用 hash_equals() 防时序攻击,不用 ==。漏掉任意一环,攻击者发个 POST 就能绕过全部风控。



















