php处理上传文件需安全提取扩展名并生成唯一文件名,先用pathinfo获取扩展名并转小写,再校验白名单,然后用时间戳+随机数、uniqid('', true)或哈希加盐生成唯一basename,最后拼接扩展名并过滤路径字符后保存。

PHP处理用户上传文件时,必须在提取原始扩展名后生成唯一、不可预测、无漏洞的存储文件名,否则可能引发覆盖攻击、路径遍历或MIME类型欺骗。
先安全提取扩展名
用 pathinfo($file['name'], PATHINFO_EXTENSION) 获取扩展名,它能正确处理多点文件名(如 archive.tar.gz)、隐藏文件(.gitignore)和无扩展名文件,避免 explode('.') 或 strrpos() 导致的截断错误。
将扩展名强制转为小写:$ext = strtolower($ext); 防止因大小写差异绕过白名单校验(例如 .PHP → .php)。
必须验证扩展名是否属于预设白名单,如 in_array($ext, ['jpg', 'png', 'pdf', 'txt']),禁止直接拼接未经校验的 $ext。
生成防冲突的唯一文件名
方法一:时间戳 + 微秒 + 随机数
使用 microtime(true) 生成浮点时间戳,乘以 10000 取整,再拼接 rand(1000, 9999),确保每毫秒内可生成多个不重复名:
$basename = (int)(microtime(true) * 10000) . rand(1000, 9999);
方法二:uniqid() + 更强熵源
调用 uniqid('', true) —— 第二个参数 true 会附加额外微秒级熵,比单纯 uniqid() 更难预测:
$basename = uniqid('', true);
【注意:uniqid('', true) 返回含小数点的字符串,需用 str_replace('.', '', ...) 清除】
方法三:结合哈希与随机盐
生成随机盐,与时间戳组合后取 SHA-256 前16位:
$salt = bin2hex(random_bytes(8));
$basename = substr(hash('sha256', time() . $salt), 0, 16);
组合完整安全文件名
第一步:确认 $ext 已通过白名单校验且非空;
第二步:生成 $basename 后,严格拼接为 $new_name = $basename . '.' . $ext;
第三步:检查 $new_name 是否含路径字符(如 ../、 、/),可用正则 preg_match('/[/\\\x00]/', $new_name) 拦截;
第四步:最终调用 move_uploaded_file($file['tmp_name'], UPLOAD_DIR . '/' . $new_name) 完成保存。
这一步不能直接用 $file['name'] 拼接,否则攻击者上传名为 “shell.php.jpg” 的文件,若仅靠后缀判断,可能被误认为图片而执行 PHP 代码。
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











