路径穿越是什么
路径穿越(又称目录遍历)指文件操作接受用户可控的路径片段,输入中的上级目录记号让访问跳出预期目录,从而读取或覆盖系统文件。它的成因是把「拼接路径」当作安全操作:只要用户能影响路径的任何一段,就可能引入跨越目录的成分。防护的核心不是过滤某个字符串,而是先做路径规范化(把相对成分解析掉),再校验结果仍落在允许的基准目录内;更彻底的做法是让用户提交逻辑标识,由服务端映射到磁盘上的随机文件名。
基本信息
| 风险类别 | OWASP Top 10 2021 A03 注入类;CWE-22 |
|---|---|
| 常见入口 | 文件下载、图片预览、附件路径、模板与日志查看、导入导出 |
| 危害 | 读取配置文件与凭据、读取源码、覆盖或写入文件 |
| 防护顺序 | 取文件名 → 规范化 → 前缀校验 → 映射到固定存储名 |
| 语言差异 | Linux 用正斜杠,Windows 同时接受反斜杠与盘符 |
| 编码变体 | 百分号编码、双重编码、Unicode 变形在解码后才生效 |
典型场景
- 附件与图片下载接口按文件名取磁盘路径
- 模板或报表功能允许指定模板文件名
- 日志查看页面按日期与文件名读取日志
- 导入功能解压压缩包时未校验包内文件名
- 静态资源代理按路径参数回源取文件
- 备份与导出功能把用户提供的名称拼进输出路径
安全实现示意(Python)
安全实现示意(Python)
import os
BASE = os.path.realpath('/srv/files')
name = os.path.basename(user_input) # 先剥离路径成分
target = os.path.realpath(os.path.join(BASE, name))
if not target.startswith(BASE + os.sep):
raise PermissionError('path out of base')
# 更稳的做法:文件名与数据库 ID 映射,磁盘文件使用 UUID 命名
# 注意 realpath 之后再比较,比较时要带路径分隔符,避免前缀相似的目录被误判绕过手法与防护要点
| 绕过手法 | 说明 | 防护要点 |
|---|---|---|
| 双重编码 | 编码后再解码成上级目录记号 | 在解码之后、调用文件系统之前校验 |
| 多级相对路径 | 用多层上级记号跳出目录 | 规范化后再校验前缀 |
| 前缀相似目录 | 目标目录名以基准目录名开头 | 比较时补上路径分隔符 |
| Windows 反斜杠 | 用反斜杠或盘符切换路径 | 统一按目标平台规则规范化 |
| 压缩包内路径 | 解压时写出到目录之外 | 解压前逐个校验条目路径 |
多轮编码(%2e%2e%2f 等)会在框架解码后变成 ../;校验必须发生在解码之后、文件系统调用之前。
最省心的方案是把用户可见名称与磁盘存储名解耦:用户传 ID 或经过校验的名称,服务端查表得到随机存储名,路径穿越在结构上就不存在了。