PHP 8.0 不处理拖放文字,所谓“拖放文字路径穿越”实为用户提交恶意路径(如../../etc/passwd)导致目录遍历;防范核心是不信任用户输入路径,须用basename()截断、realpath()校验物理路径、白名单约束,并推荐以ID/哈希代替路径传递。

PHP 8.0 本身不处理“拖放文字”这种前端交互行为,所谓“拖放文字路径穿越”实际是用户通过表单、URL 参数或文件上传等途径提交恶意路径字符串(如 ../../etc/passwd),后端未做校验就直接用于文件操作,从而触发目录遍历。防范核心在于:**不信任任何用户输入的路径片段,强制剥离、规范化、白名单约束**。
用 basename() 强制截断路径,只留文件名
这是最常用也最直接的第一道防线,适用于读取或写入上传目录下单一文件的场景:
- 调用 basename($_GET['file']) 或 basename($_POST['path']),它会丢弃所有
/、../、./等路径成分,只返回末尾合法文件名(如../../config.php→config.php) - 必须配合白名单校验:用正则确保文件名只含安全字符,例如 preg_match('/^[a-zA-Z0-9._-]{1,255}$/', $safe_name)
- 额外检查是否为
.或..:if (in_array($safe_name, ['.', '..'], true)) { die('非法文件名'); } - 注意:若用户传的是 URL 编码路径(如
%2e%2e%2f),需先 urldecode() 再用 basename()
用 realpath() + 白名单根目录双重确认
basename() 无法防御符号链接、Unicode 绕过或空字节攻击,必须结合 realpath() 做最终物理路径校验:
修复 Core bailout 状态损坏安全问题,以及 BZ2、DOM、PDO、Opcache/JIT 内存与执行错误,改进 Unicode 字素处理、URL 解析、TLS 流状态、信号分发及会话错误处理。
- 定义绝对上传根目录:define('ROOT_DIR', '/var/www/uploads/');(末尾斜杠不能少)
- 拼接并解析:$full_path = realpath(ROOT_DIR . basename($_GET['file']));
- 严格检查:if ($full_path === false || !str_starts_with($full_path, realpath(ROOT_DIR))) { die('非法访问'); }(PHP 8.0+ 推荐
str_starts_with,兼容性优于strpos === 0) - realpath() 返回 false 表示路径不存在、不可访问或含非法符号,该判断绝不能省略
彻底规避:用 ID 或哈希代替路径传递
最安全的方式是不让用户接触任何路径字符串:
立即学习“PHP免费学习笔记(深入)”;
- 用户请求时只传一个随机 ID(如
?id=7f3a9b2d)或短哈希 - 后端查数据库或缓存,获取该 ID 对应的真实存储路径(如
/var/www/uploads/7f3a9b2d_report.pdf) - 路径由服务端完全控制,用户无法推测、拼接或越权,天然免疫路径穿越
- 适合文件下载、附件预览等业务,需配合唯一索引和访问权限逻辑
上传文件必须用 move_uploaded_file() 并重命名
用户上传的文件名尤其危险,不能原样保存:
- 禁止使用
$_FILES['file']['name']直接拼路径;必须用 basename() 提取后,再用 uniqid() . '.' . pathinfo(..., PATHINFO_EXTENSION) 生成随机文件名 - 目标路径必须硬编码为白名单目录,如 $target = ROOT_DIR . $random_name;
- 务必使用 move_uploaded_file($_FILES['file']['tmp_name'], $target),而非
copy()或file_put_contents() - 上传目录在 Web 服务器层禁用脚本执行(如 Nginx 中配置
location ~ \.php$ { deny all; })


















