文件上传安全处理
文件上传功能如果处理不当,可能导致严重的安全漏洞。本文介绍安全的文件上传实现方法。
基础安全上传代码
php
function uploadFile($file, $uploadDir = 'uploads/') {
// 1. 检查上传错误
if ($file['error'] !== UPLOAD_ERR_OK) {
throw new Exception('上传失败,错误码:' . $file['error']);
}
// 2. 验证文件大小(限制 2MB)
$maxSize = 2 * 1024 * 1024;
if ($file['size'] > $maxSize) {
throw new Exception('文件过大,最大允许 2MB');
}
// 3. 使用 finfo 检测真实 MIME 类型
$finfo = new finfo(FILEINFO_MIME_TYPE);
$mimeType = $finfo->file($file['tmp_name']);
$allowedTypes = [
'image/jpeg' => 'jpg',
'image/png' => 'png',
'image/gif' => 'gif',
'application/pdf' => 'pdf'
];
if (!isset($allowedTypes[$mimeType])) {
throw new Exception('不支持的文件类型');
}
// 4. 生成安全的文件名(不使用原始文件名)
$extension = $allowedTypes[$mimeType];
$filename = bin2hex(random_bytes(16)) . '.' . $extension;
$filepath = $uploadDir . $filename;
// 5. 确保目录存在且可写
if (!is_dir($uploadDir)) {
mkdir($uploadDir, 0755, true);
}
// 6. 移动文件
if (!move_uploaded_file($file['tmp_name'], $filepath)) {
throw new Exception('文件保存失败');
}
return $filepath;
}
// 使用示例
try {
$path = uploadFile($_FILES['avatar']);
echo "上传成功:$path";
} catch (Exception $e) {
echo "错误:" . $e->getMessage();
}图片额外处理(防止恶意图片)
php
function processImage($sourcePath, $destPath, $maxWidth = 1920, $maxHeight = 1080) {
// 使用 getimagesize 验证图片完整性
$imageInfo = getimagesize($sourcePath);
if ($imageInfo === false) {
throw new Exception('无效的图片文件');
}
// 重新生成图片(清除可能的恶意代码)
list($width, $height, $type) = $imageInfo;
switch ($type) {
case IMAGETYPE_JPEG:
$src = imagecreatefromjpeg($sourcePath);
break;
case IMAGETYPE_PNG:
$src = imagecreatefrompng($sourcePath);
break;
default:
throw new Exception('不支持的图片格式');
}
// 缩放处理
$ratio = min($maxWidth / $width, $maxHeight / $height, 1);
$newWidth = (int)($width * $ratio);
$newHeight = (int)($height * $ratio);
$dst = imagecreatetruecolor($newWidth, $newHeight);
imagecopyresampled($dst, $src, 0, 0, 0, 0, $newWidth, $newHeight, $width, $height);
// 保存新图片
imagejpeg($dst, $destPath, 90);
imagedestroy($src);
imagedestroy($dst);
// 删除原始上传文件
unlink($sourcePath);
}安全配置检查
php
// php.ini 推荐配置
file_uploads = On
upload_max_filesize = 2M
post_max_size = 8M
upload_tmp_dir = /var/www/uploads/tmp
// .htaccess 保护上传目录
// 放在 uploads/.htaccess
<FilesMatch "\.(php|php5|phtml|pl|py|jsp|asp|sh|cgi)$">
Order allow,deny
Deny from all
</FilesMatch>常见攻击防范
| 攻击类型 | 防范方法 |
|---|---|
| 双扩展名攻击 | 不使用原始文件名,强制指定扩展名 |
| MIME 伪造 | 使用 finfo 检测真实类型 |
| 文件包含漏洞 | 上传目录不允许执行脚本 |
| 目录遍历 | 验证文件名,移除路径分隔符 |
| 文件大小攻击 | 限制文件大小并验证 |
注意事项
- 永远不要 信任
$_FILES['name'],使用生成的文件名 - 上传目录应该设置为不可执行脚本
- 考虑使用云存储(OSS/S3)代替本地存储
- 敏感文件建议存储在 Web 根目录之外