- 目的:确保 iOS 客户端上传的图片在 PHP 服务器端既稳定又安全,防止任意文件写入、代码执行或资源耗尽。
- 适用场景:使用 multipart/form-data POST 上传图片(UIImagePicker 或 NSURLSession/Alamofire 上传)。
- 总原则:验证来源、验证 MIME 与内容、规范/唯一化文件名、限制大小与频率、最小权限存储并通过后端控制访问。
- 必要配置:在 php.ini 中设置 upload_max_filesize 与 post_max_size(例如 10M)、max_file_uploads、memory_limit 合理值。重启 PHP-FPM/Apache。
- 建议:upload_tmp_dir 指定为私有目录,确保 tmp 目录权限仅限 Web 用户可写。使用 ini_get('upload_max_filesize') 在代码中检测并给客户端友好提示。
- 目录位置:尽量将上传目录放在 webroot 之外,例如 /var/www/uploads 或 /srv/uploads,并通过脚本控制访问。
- 权限设置(Linux 举例):chown www-data:www-data /srv/uploads;chmod 750 /srv/uploads(目录);上传后文件 chmod 0644。避免 777。
- SELinux/AppArmor:如启用,请为 PHP-FPM/Apache 进程配置允许写入该目录的策略。
- 检查请求方法与登录态:if ($_SERVER['REQUEST_METHOD'] !== 'POST') 403;验证 JWT/session 权限。
- 检查文件字段与错误码:if (!isset($_FILES['image'])) 返回错误;检查 $_FILES['image']['error'] === UPLOAD_ERR_OK。
- 限制大小:if ($_FILES['image']['size'] > 5*1024*1024) 拒绝(示例 5MB)。
- 使用 Fileinfo:$finfo = finfo_open(FILEINFO_MIME_TYPE); $mime = finfo_file($finfo, $_FILES['image']['tmp_name']); finfo_close($finfo).
- 可接受类型:image/jpeg、image/png、image/gif(根据需求);iOS HEIC: image/heif 或 image/heic(服务器默认可能不支持,需要额外转换或拒绝并提示客户端转换)。
- 双重验证:再使用 getimagesize($_FILES['image']['tmp_name']) 检测宽高与 MIME 是否一致,若不一致拒绝。
- 不信任客户端文件名:不要直接使用 $_FILES['image']['name']。
- 规范化:根据 MIME 推断扩展名(例:image/jpeg -> .jpg),并过滤中文/特殊字符。
- 生成唯一名:$basename = time().'_'.bin2hex(random_bytes(8)).$ext; 或使用 UUID。确保长度合理且无路径分隔符。
- 避免双扩展名:严格用映射后缀替换原名扩展,以防 .php.jpg 等绕过。
- 使用 move_uploaded_file 进行原子移动:$target = $uploadDir.'/'.$basename; if (!move_uploaded_file($tmp, $target)) 记录日志并告警。
- 写入后再设置权限与属主:chown/chmod,记录存储路径到数据库并存储来源用户 id、上传时间、MIME、尺寸等元数据。
- 日志:记录 IP、用户 Agent、文件大小、校验结果,便于审计与异常检测。
- 非直接静态访问:若目录在 webroot 外,提供图片访问接口,如 /download.php?id=xxx,接口校验权限后使用 readfile。
- 设置缓存头:合理设置 Cache-Control、ETag,防止被滥用同时提高性能。
- 防止 XSS:Content-Type 固定由后端返回,不直接信任原始文件的扩展名或上传者提供的 MIME。
- 速率限制:在 Nginx 或应用层添加 IP/用户上传频率限制,防止流量突发。
- 病毒扫描:将上传文件推送到 ClamAV 或第三方服务异步扫描,扫描结果不通过则删除并告警。
- 异步处理:若需转码/生成缩略图,使用队列(RabbitMQ/Redis Queue)与 worker,避免阻塞请求。
问:上传时报错 “UPLOAD_ERR_PARTIAL” 或文件丢失,如何排查?
答:检查客户端网络稳定性与超时设置;检查 php.ini 的 post_max_size 与 upload_max_filesize,确认临时目录空间与权限;查看 PHP/NGINX 错误日志与 access 日志,定位是否被反向代理截断。
问:iOS 上传 HEIC 图片,服务器不识别怎么办?
答:两种方案:1)在 iOS 端将 HEIC 转为 JPEG/PNG 再上传(推荐);2)服务器端安装 ImageMagick/libheif,并在接收后用命令行 convert 或 magick 转码为 JPEG,然后再做校验存储。注意转码后保留原尺寸与 EXIF(如需要)。
问:怎样综合防护以保证长期稳定与安全?
答:坚持多层防护:严格认证与权限校验;内容与 MIME 双重校验;唯一化与安全的文件名;限制大小与频率;把上传目录放在非 webroot 并通过后端控制访问;配合异步扫描、日志告警与监控(磁盘、流量、异常上传频次)。定期演练恢复与清理旧文件。