Yii2文件上传安全验证必须覆盖类型、大小、内容真实性、存储路径和执行权限五个维度,缺一不可;需组合使用file验证器的extensions、mimeTypes、maxSize、checkExtensionByMimeType等规则,并强制重命名、隔离存储、二次校验及Nginx层脚本拦截。

Yii2文件上传安全验证必须覆盖类型、大小、内容真实性、存储路径和执行权限五个维度,缺一不可,否则恶意用户可能上传WebShell或触发XXE攻击。
第一步:创建带多重校验的上传模型
在app/models/UploadForm.php中定义模型,关键不是只限制扩展名,而是组合验证规则:
① 声明$file属性并启用file验证器,同时开启checkExtension和mimeTypes双重检测:
public $file;
② 在rules()中写入完整校验链:[['file'], 'file', 'skipOnEmpty' => false, 'extensions' => 'jpg,png,gif,pdf,xml', 'mimeTypes' => ['image/jpeg', 'image/png', 'image/gif', 'application/pdf', 'application/xml'], 'maxSize' => 5242880, 'checkExtensionByMimeType' => true]。
③ 【必须添加'checkExtensionByMimeType' => true】,否则攻击者把PHP文件改成.jpg后缀就能绕过扩展名检查——Yii2默认只查后缀,不读文件头魔数。
④ 若上传XML,额外加一条规则防止XXE:[['file'], 'safe'],后面在upload()方法里手动调用libxml_disable_entity_loader(true)。
第二步:前端表单强制启用multipart和CSRF防护
视图文件中渲染表单时,漏掉任一配置都会导致400错误或被绕过:
使用ActiveForm::begin()时必须传入enctype参数:['options' => ['enctype' => 'multipart/form-data']],否则$_FILES为空,框架报错提示模糊(如“Unable to verify your data submission”)。
Yii2默认开启CSRF验证,所以表单必须包含令牌——直接用$form->field($model, 'file')->fileInput()会自动注入隐藏域,不用手写csrfMetaTags。
如果禁用CSRF(不推荐),要在控制器中显式关闭:public function init() { parent::init(); $this->enableCsrfValidation = false; },但这样会暴露整个控制器受CSRF攻击风险。
第三步:服务端重命名+隔离存储+内容再校验
文件保存前必须切断原始文件名与路径的关联,否则存在路径遍历或覆盖漏洞:
方法一:用uniqid() . '.' . $file->extension生成新名,【绝对不要用$file->name拼接路径】,因为攻击者可构造../../etc/passwd.jpg突破目录限制。
方法二:保存到Web根目录外的绝对路径,例如Yii::getAlias('@app') . '/private_uploads/',该目录不能被HTTP直接访问。
图片类文件需二次校验真实类型:getimagesize($file->tempName)返回非false才允许入库,防止在PNG末尾注入PHP代码。
XML文件解析前必须执行:libxml_disable_entity_loader(true); $xml = simplexml_load_file($file->tempName);,否则file:///协议可读取服务器任意文件。
第四步:Nginx层加固上传目录执行权限
仅靠PHP层防护不够,必须配合Web服务器限制:
在Nginx配置中为上传目录添加location ~ ^/uploads/.*\.(php|phar|phtml|pl|py|jsp|sh|bash)$ { deny all; },阻止脚本执行。
同时设置client_max_body_size 5M;,与PHP的post_max_size和upload_max_filesize保持一致,避免大文件耗尽内存。
若使用Apache,在.htaccess中加入:
Options -ExecCGI<br>RemoveHandler .php .phtml .pl .py .jsp。


















