move_uploaded_file是唯一安全的PHP文件上传落地函数,因其强制校验$_FILES['tmp_name']是否为真实HTTP POST上传文件,伪造路径(如/etc/passwd)时直接返回false;而copy等函数无此校验,易导致任意文件写入漏洞。

move_uploaded_file 是唯一安全、合法的 PHP 文件上传落地函数,其他如 copy、rename 或直接 fopen+fwrite 都会绕过上传校验,导致任意文件写入漏洞。
为什么必须用 move_uploaded_file 而不是 copy
该函数内部强制检查 $_FILES['xxx']['tmp_name'] 是否为真实 HTTP POST 上传产生的临时文件。若路径是伪造的(比如用户传入 /etc/passwd),move_uploaded_file 直接返回 false;而 copy 会照常执行,造成严重安全风险。
-
move_uploaded_file仅接受$_FILES中的tmp_name值,拒绝任何其他字符串路径 - 它还会校验目标目录是否存在、是否有写权限、是否跨分区(部分系统下失败)
- 错误时不会抛出异常,只返回
false,必须显式判断返回值
$_FILES 数组结构容易搞错的三个地方
多文件或单文件场景下,$_FILES 的键层级常被误读,导致 size、error 取错位置。
- 单文件:
$_FILES['avatar']['name']、$_FILES['avatar']['size']、$_FILES['avatar']['tmp_name']是同级字段 - 多文件(
name="files[]"):$_FILES['files']['name'][0]和$_FILES['files']['size'][0]才对应第 1 个文件,不能写成$_FILES['files']['name'][0]['size'] -
$_FILES['x']['error']必须先检查是否等于UPLOAD_ERR_OK(即0),否则tmp_name不可信,可能为空或非法
move_uploaded_file 失败的四个高频原因
返回 false 并不等于“没传上来”,可能是中间环节卡住,需逐层排查。
立即学习“PHP免费学习笔记(深入)”;
- 目标路径目录不存在且未创建:用
is_dir($dir) || mkdir($dir, 0755, true)确保父级路径就绪 - 文件名含非法字符或路径遍历:如
../../etc/passwd,务必用basename()截取文件名,禁用原始$_FILES['x']['name'] - PHP 配置限制:
upload_max_filesize和post_max_size都要够大,且post_max_size >= upload_max_filesize - 临时目录不可写或磁盘满:检查
sys_get_temp_dir()返回路径的权限与空间,尤其 Docker 容器中常被忽略
最易被跳过的其实是 basename() 这一步——文件名里带 /(比如用户改包重命名成 2024/08/photo.jpg)会导致 move_uploaded_file 报 No such file or directory,但错误码仍是 0,表面看“上传成功”实则静默失败。



















