本文目录导读:

- 文件上传错误代码验证($_FILES[”error”])
- 文件大小验证
- 文件类型验证(MIME 类型)
- 文件扩展名验证
- 重写文件名(防止路径穿越与冲突)
- 图片内容验证(针对图片)
- 其他安全与业务规则
- 完整的安全验证流程(推荐顺序)
- 常见误区和陷阱
在 PHP 中进行文件验证(通常指文件上传时的验证),主要目的是确保上传的文件安全、有效且符合预期,以下是常见且必须的 PHP 文件验证规则,通常按顺序执行:
文件上传错误代码验证($_FILES[”error”])
这是第一道防线,PHP 会根据上传是否成功自动填充一个错误代码。
UPLOAD_ERR_OK(值 0):无错误,文件上传成功。UPLOAD_ERR_INI_SIZE(1):文件超过php.ini中upload_max_filesize限制。UPLOAD_ERR_FORM_SIZE(2):文件超过 HTML 表单中MAX_FILE_SIZE指定的大小。UPLOAD_ERR_PARTIAL(3):文件只有部分被上传(网络问题)。UPLOAD_ERR_NO_FILE(4):没有上传文件(用户未选择)。UPLOAD_ERR_NO_TMP_DIR(6):缺少临时文件夹(服务器环境问题)。UPLOAD_ERR_CANT_WRITE(7):写入磁盘失败。
示例代码:
if ($_FILES['file']['error'] !== UPLOAD_ERR_OK) {
die('上传失败,错误代码:' . $_FILES['file']['error']);
}
文件大小验证
防止用户上传过大文件撑爆服务器。
- PHP 配置限制(服务器端):
upload_max_filesize和post_max_size。 - 代码验证(推荐): 使用
$_FILES['file']['size']进行校验。
$maxSize = 2 * 1024 * 1024; // 2MB
if ($_FILES['file']['size'] > $maxSize) {
die('文件大小不能超过 2MB');
}
文件类型验证(MIME 类型)
注意: 不能仅依赖 $_FILES['file']['type'],因为这个值由客户端(浏览器)控制,极易被篡改,不安全的做法如下:
// ❌ 不安全:客户端可以随意修改这个值
if ($_FILES['file']['type'] !== 'image/jpeg') {
die('只允许 JPEG 图片');
}
正确做法:使用服务器端函数检测。
- 图片文件:
getimagesize()或exif_imagetype()。 - 通用文件:
finfo_open()与finfo_file()(基于文件魔数魔术字节,更可靠)。
// ✅ 安全:使用服务器端真实文件检测
$finfo = finfo_open(FILEINFO_MIME_TYPE);
$mimeType = finfo_file($finfo, $_FILES['file']['tmp_name']);
finfo_close($finfo);
$allowedTypes = ['image/jpeg', 'image/png'];
if (!in_array($mimeType, $allowedTypes)) {
die('只允许 JPEG 或 PNG 图片');
}
文件扩展名验证
防止用户上传 shell.php 并伪装成图片(双重校验)。
$extension = strtolower(pathinfo($_FILES['file']['name'], PATHINFO_EXTENSION));
$allowedExts = ['jpg', 'jpeg', 'png', 'gif'];
if (!in_array($extension, $allowedExts)) {
die('不允许的文件扩展名');
}
重写文件名(防止路径穿越与冲突)
用户上传的文件名可能包含恶意字符(如 ../../etc/passwd)或中文字符。永远不要使用原始文件名保存到磁盘。
- 方案: 生成唯一文件名(UUID、时间戳 + 随机数)。
- 安全存储: 将文件移出
document_root(网站根目录),或设置http://example.com/uploads/目录下.htaccess禁止执行 PHP。
$newFileName = uniqid() . '.' . $extension; // 如 61f8a3b1c2d3e.jpg
if (!move_uploaded_file($_FILES['file']['tmp_name'], 'uploads/' . $newFileName)) {
die('文件保存失败');
}
验证(针对图片)
如果只允许图片,除了 MIME 检测,还需验证图片完整性。
$imageInfo = getimagesize($_FILES['file']['tmp_name']);
if ($imageInfo === false) {
die('文件不是有效的图片');
}
// 可以继续检查宽度、高度等
if ($imageInfo[0] > 2000 || $imageInfo[1] > 2000) {
die('图片尺寸过大');
}
其他安全与业务规则
- 目录权限检查: 确保
uploads/目录可写,且不允许执行。 - 文件数量限制: 防止用户恶意批量上传。
- 黑名单检测: 禁止包含 PHP 代码片段(如
<?php)在文件内容中(用于简易文本验证)。 - SVG 文件警告: SVG 可能包含 JavaScript(XSS 攻击),需严格消毒或禁用。
- 临时文件清理: 如果未用
move_uploaded_file(),临时文件会在脚本结束后自动删除。
完整的安全验证流程(推荐顺序)
- 检查
$_FILES['file']['error']是否为UPLOAD_ERR_OK。 - 检查文件大小
$_FILES['file']['size']是否符合限制。 - 使用
finfo检测真实的 MIME 类型。 - 检查文件扩展名(作为辅助手段)。
- 如果是图片,使用
getimagesize()验证图片完整性。 - 生成唯一文件名(避免原有名称)。
- 使用
is_uploaded_file()+move_uploaded_file()移动文件(确保是上传的文件)。
常见误区和陷阱
- 🚫 不要使用
$_FILES['file']['type']作唯一判断。 - 🚫 不要使用黑名单扩展名(如
.php3、.phtml仍可能被解析)。 - 🚫 不要允许
.htaccess或.user.ini上传。 - ✅ 推荐:上传目录关闭 PHP 执行权限(Nginx 中
location ~ \.php$ { deny all; })。 - ✅ 推荐:将文件存储到非 Web 可访问目录,通过 PHP 代理脚本读取并提供下载。
这些规则不仅用于上传,也适用于脚本中处理文件时的基本验证逻辑。