应使用realpath()结合白名单根目录校验,或basename()截断再拼接,或ID映射替代路径传递,上传文件必须剥离路径并重命名,Web服务器层需协同限制访问范围。

如果您在PHP应用中直接拼接用户输入的文件路径参数(如$_GET['file']或$_POST['path']),攻击者可能通过构造类似../../etc/passwd的输入读取系统敏感文件,则存在目录穿越安全漏洞。以下是防止该漏洞的多种有效方法:
一、使用realpath()结合白名单根目录前缀校验
该方法通过将用户输入路径规范化为真实绝对路径,并严格比对其是否位于授权根目录内,可抵御符号链接、多层..、URL编码绕过及空字节截断等常见攻击变体。realpath()会解析所有相对路径组件和软链接,若路径越权或不存在则返回false,必须显式检查该返回值。
1、获取授权根目录的真实绝对路径:/var/www/uploads/(注意末尾必须带斜杠)。
2、拼接用户输入并调用realpath():$real_path = realpath($root_dir . $_GET['file']);。
3、检查realpath()返回值是否为false,若是则拒绝请求。
4、使用strpos($real_path, $root_dir) === 0验证路径是否以白名单根目录开头(PHP 7.x);PHP 8.0+推荐使用str_starts_with($real_path, $root_dir)。
5、仅当上述全部校验通过后,才执行file_get_contents()或readfile()等文件操作。
二、强制使用basename()截断原始输入再拼接
该方法适用于仅需访问上传目录下一级文件名的场景。basename()剥离路径中所有前置部分,只保留末段文件名,可有效阻断路径向上跳转,但必须配合白名单根目录使用,且不能单独用于防御编码绕过或空字节攻击。
1、从用户输入中提取纯净文件名:$safe_name = basename($_GET['file']);。
2、手动拼接至预设白名单目录:$full_path = '/var/www/uploads/' . $safe_name;。
3、调用is_file($full_path)和is_readable($full_path)双重确认文件存在且可读。
4、确保$safe_name不为空字符串或点号(.或..),可追加if (in_array($safe_name, ['.', '..'], true)) { die('Invalid filename'); }。
三、采用ID映射或哈希路径替代原始文件名传递
该方案彻底消除用户控制路径字符串的可能性,属于纵深防御中的根本性规避策略。业务逻辑通过唯一ID或哈希值查表获取对应磁盘路径,用户无法通过参数推测或操控实际文件位置。
1、将合法文件信息存入数据库,字段包括id、original_name、stored_path(如/var/www/uploads/7f3a9b2d_report.pdf)。
2、接收用户请求参数file_id=123,查询数据库获得stored_path值。
3、对查询结果进行存在性与权限校验:file_exists($stored_path) && is_readable($stored_path)。
4、直接使用查得的$stored_path执行文件读取,完全不参与任何路径拼接逻辑。
四、上传文件名强制剥离路径并重命名
针对$_FILES['file']['name']可能被伪造为../../.env的风险,必须在保存前清除所有路径信息并生成不可预测的新文件名,避免攻击者利用原始文件名触发遍历。
1、调用basename($_FILES['file']['name'])提取客户端声明的文件名。
2、使用pathinfo()分离基础名与扩展名:$ext = pathinfo($basename, PATHINFO_EXTENSION);。
3、生成唯一安全文件名:$new_name = uniqid() . '_' . md5(microtime()) . '.' . strtolower($ext);。
4、将文件移入白名单目录:move_uploaded_file($_FILES['file']['tmp_name'], '/var/www/uploads/' . $new_name);。
5、保存$new_name到数据库或会话中,后续访问均基于该名称而非用户输入。
五、Web服务器层协同限制访问范围
即使PHP代码存在疏漏,Apache或Nginx的配置也能构成最后一道防线。通过限制PHP进程可访问的文件系统范围,可大幅缩小攻击面,尤其能阻止对/etc/、/root/等高危路径的尝试。
1、在PHP配置中启用open_basedir,设置为/var/www/:/tmp/等必要目录,多个路径用冒号分隔。
2、在Nginx中对上传目录外的敏感路径返回403:location ^~ /etc/ { return 403; }。
3、在Apache中使用指令禁止访问DocumentRoot之外的路径:Require all denied。
4、确保上传目录(如/var/www/uploads/)的文件权限为0750,属主为Web服务运行用户,禁止其他用户读取。
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











