laravel图片上传必须禁用原始文件名、用finfo校验真实mime、重建图像剥离元数据,并配置web中间件与服务器脚本拦截。

在 Laravel 中实现图片上传时,若未配置严格验证与安全防御机制,攻击者可能上传伪装成 JPG 的 PHP WebShell 文件,导致服务器被远程执行代码。真实生产环境中,仅靠扩展名白名单或 MIME 类型校验根本无法阻止此类攻击。
防止伪造图片文件上传
第一步:禁用客户端提交的原始文件名,强制使用安全生成的文件名
不要调用 $request->file('image')->getClientOriginalName(),该方法返回用户可控字符串,含路径遍历(如 ../../.env)或恶意扩展名(如 shell.php.jpg)风险。
第二步:用 storeAs() 时,必须配合 getClientOriginalExtension() 提取真实扩展名,并手动映射为白名单内格式:$ext = in_array($request->file('image')->getClientOriginalExtension(), ['jpg', 'jpeg', 'png', 'gif', 'webp']) ? $request->file('image')->getClientOriginalExtension() : 'png';
第三步:生成唯一安全文件名,避免重名与覆盖:$filename = uniqid('img_') . '_' . time() . '.' . $ext;
第四步:调用 storeAs() 并指定磁盘为 public:$path = $request->file('image')->storeAs('uploads', $filename, 'public');
双重 MIME 类型校验(绕过前端 accept 属性)
方法一:Laravel 内置规则 + 手动头检测
验证规则中必须同时包含 image 和 mimes:jpeg,png,gif,webp,但这两项仅校验 HTTP 头部 MIME,不可信。
方法二:用 finfo 实际读取文件头(关键防线)
在控制器中添加:
$finfo = new finfo(FILEINFO_MIME_TYPE);<br>$realMime = $finfo->file($request->file('image')->getRealPath());<br>if (!in_array($realMime, ['image/jpeg', 'image/png', 'image/gif', 'image/webp'])) {<br> return back()->withErrors(['image' => '文件内容与声明类型不符']);<br>}
【必须执行 getRealPath(),否则 finfo 无法读取临时文件内容】
剥离 EXIF 与隐藏元数据
直接存储用户上传的原始图片,等于将相机型号、GPS 坐标、编辑软件等敏感信息暴露在 Web 路径下。
安装 Intervention Image:composer require intervention/image
上传后立即重建图像:
use Intervention\Image\Facades\Image;<br>$img = Image::make($request->file('image'))->encode('jpg', 85);<br>$safePath = 'uploads/' . uniqid('clean_') . '.jpg';<br>Storage::disk('public')->put($safePath, $img->encoded);
这一步会丢弃所有非像素数据,只保留压缩后的 JPEG 数据流。
限制上传上下文与权限隔离
✅ 正确做法:所有上传路由必须绑定到带 CSRF 保护的 Web 中间件组
检查 app/Http/Kernel.php 中该路由是否在 web 组内——只有 web 组默认启用 VerifyCsrfToken 和 EncryptCookies。
❌ 错误配置示例:
若将上传路由写在 api 路由文件中且未显式添加 auth:sanctum 或 throttle:api,则既无 CSRF 保护,也无速率限制。
运行 php artisan route:list 确认上传路由中间件列显示 web,而非 api 或空值。
禁止可执行脚本通过 .htaccess 或 Nginx 解析
即使文件已存入 public/uploads/,Apache 仍可能将 shell.php.jpg 当作 PHP 执行(若开启 AddType application/x-httpd-php .jpg)。
在 public/uploads/.htaccess 中强制禁止脚本解析:
Options -ExecCGI -Indexes<br>RemoveHandler .php .phtml .php3 .php4 .php5 .php7 .php8<br>php_flag engine off<br>SetHandler none
Nginx 用户需在 server 块内添加:
location ^~ /uploads/ {<br> location ~ \.(php|pl|py|jsp|asp|sh|cgi)$ {<br> return 403;<br> }<br>}











