yii文件上传需严格校验:启用扩展名白名单(用$file->extension而非pathinfo)、重命名文件(禁用原始文件名)、限制上传目录权限(移出webroot或nginx/apache隔离)、辅以文件名过滤,缺一不可。

Yii框架上传文件时若未对用户提交的文件名做严格校验,攻击者可构造类似../../etc/passwd的恶意文件名,导致文件被写入Web目录之外的敏感路径,直接触发路径遍历漏洞。
确认是否启用文件上传白名单机制
打开应用配置文件config/web.php或对应环境的配置,检查yii\web\UploadedFile相关处理逻辑是否强制指定了允许的扩展名列表。若仅依赖前端JS校验或后端简单字符串匹配(如strpos($name, '..') === false),该防护形同虚设。
必须在控制器接收上传后、保存前,调用UploadedFile::getInstance()获取实例,并立即验证$file->extension是否属于预设白名单数组,例如['jpg', 'png', 'pdf']。
不要用pathinfo($file->name, PATHINFO_EXTENSION)提取扩展名——攻击者可上传shell.php.jpg绕过,Yii的$file->extension会自动取真实扩展名,更可靠。
重命名上传文件并禁用原始文件名
第一步:生成唯一安全文件名,例如使用Yii::$app->security->generateRandomString(32)配合时间戳和哈希;
第二步:拼接新文件名时【绝对禁止拼接原始$_FILES['xxx']['name']中的任意字段】,包括basename、pathinfo结果、甚至解码后的URL字符串;
第三步:调用$file->saveAs('@webroot/uploads/' . $safeName . '.' . $file->extension)保存——注意@webroot是Yii内置别名,指向Web可访问根目录,确保路径开头不含../或~等危险字符;
这一步操作起来很简单,直接把文件拖进去就行,但若漏掉任何一环,就等于给攻击者留了后门。
限制上传目录的父级路径访问权限
方法一:在Web服务器层面隔离上传目录
Nginx配置中为/uploads/位置块添加location ^~ /uploads/ { deny all; },再单独为允许访问的静态资源(如图片)配置location ~* \.(jpg|jpeg|png|gif)$ { };
Apache需在.htaccess中启用Options -Indexes并拒绝执行PHP:<files> Require denied </files>;
方法二:将上传目录移出Web根目录
修改@webroot别名指向/var/www/myapp/web,同时新增别名@uploadPath指向/var/www/myapp/runtime/uploads,保存文件时用$file->saveAs('@uploadPath/' . $safeName);
【关键前提:确保runtime目录权限为750且属主为Web服务器运行用户(如www-data)】,否则文件写入失败。
启用Yii内置的文件名规范化过滤
在控制器中处理上传前,插入以下代码:
$fileName = $file->name;
$fileName = str_replace(['..', './', '../'], '', $fileName);
$fileName = preg_replace('/[^a-zA-Z0-9._-]/', '_', $fileName);
这三行代码能清除常见遍历符号和非法字符,但【不能替代白名单+重命名】,仅作为最后一道辅助防线;
若跳过白名单校验直接使用此过滤后的文件名保存,仍可能被%2e%2e%2f(URL编码的../)绕过。











