PHP:如何为图片上传功能正确检查文件MIME类型?
在PHP图片上传中添加双重MIME类型检查的实现方案
你目前的扩展名校验已经做了基础防护,但确实,只靠扩展名很容易被绕过——攻击者可以把恶意文件改个后缀就蒙混过去。所以在扩展名检查的前后加上基于文件真实内容的MIME类型校验,能大大提升上传安全性。下面是修改后的完整代码,我会标注清楚新增的部分:
首先,先定义允许的MIME类型白名单(和你的扩展名白名单对应):
$aExtensionWhitelist = ['png', 'jpg', 'jpeg']; $aMimeWhitelist = ['image/png', 'image/jpeg', 'image/jpg']; // 对应允许的图片MIME类型
然后是修改后的上传循环代码:
<?php $iAmountOfFiles = count($_FILES['Filename']['name']); // 初始化finfo对象,用于读取文件真实MIME类型 $finfo = new Finfo(FILEINFO_MIME_TYPE); while($iAmountOfFiles >= 1) { $iAmountOfFiles--; $tmpFilePath = $_FILES['Filename']['tmp_name'][$iAmountOfFiles]; // --- 第一步:扩展名检查前的MIME校验 --- $fileMime = $finfo->file($tmpFilePath); if(!in_array($fileMime, $aMimeWhitelist)) { echo "Bestands type niet toegestaan (ongeldige MIME)"; continue; } $aFileProperties = pathinfo($_FILES['Filename']['name'][$iAmountOfFiles]); if(!in_array(strtolower($aFileProperties["extension"]), $aExtensionWhitelist)) { echo "Bestands type niet toegestaan (ongeldige extensie)"; continue; } $sTarget = ROOT.BACKEND."/pages/bezienswaardigheden-toevoegen/uploads/"; $sUniqueFileNameHash = hash('adler32', time().rand()); $Filename = basename($aFileProperties["filename"]."-".$sUniqueFileNameHash.".".strtolower($aFileProperties["extension"])); // Writes the Filename to the server if(move_uploaded_file($tmpFilePath, $sTarget.$Filename)) { // --- 第二步:文件存入服务器后的二次MIME校验 --- $uploadedFileMime = $finfo->file($sTarget.$Filename); if(!in_array($uploadedFileMime, $aMimeWhitelist)) { // 如果校验失败,删除已上传的恶意文件 unlink($sTarget.$Filename); echo "Bestands type niet toegestaan (upload na verificatie ongeldig)"; continue; } // 这里继续你的数据库存储逻辑... } else { echo "Upload mislukt"; } } ?>
关键细节说明:
- 用
Finfo类获取真实MIME类型:不同于$_FILES['Filename']['type'](这是客户端浏览器提供的,很容易伪造),Finfo会读取文件的二进制头部信息来判断真实类型,安全性更高。 - 前置校验:在处理文件扩展名和生成文件名之前就做MIME检查,能提前拦截恶意文件,减少不必要的资源消耗。
- 后置校验:文件上传到服务器后再次校验,防止文件在传输过程中被篡改,或者出现其他异常情况,双重保险。如果校验失败,记得删除已经上传的文件,避免恶意文件留在服务器上。
内容的提问来源于stack exchange,提问作者emmea
相关产品推荐
相关产品推荐

