本文目录导读:

- 严格的基础防范:禁止拼接用户输入到文件路径
- 输入验证与净化(Input Validation & Sanitization)
- 路径规范化与安全检查(Canonicalization)
- 使用安全的API库
- 部署环境配置(多层防御)
- 常见绕过手法与对应防御
路径遍历漏洞(Path Traversal)是一种常见的Web安全漏洞,攻击者通过操纵文件路径参数(如 ../../../etc/passwd)来访问或执行服务器上未授权的文件,要有效规避此类漏洞,需从输入验证、文件系统操作隔离和应用架构设计三个层面入手。
以下是具体的安全编码与配置策略:
严格的基础防范:禁止拼接用户输入到文件路径
最根本的防御:不要直接使用用户提供的字符串来构造文件路径,如果业务逻辑必须根据用户请求读取文件,请遵循以下原则。
输入验证与净化(Input Validation & Sanitization)
如果无法完全避免用户输入参与路径构建,必须进行严格的过滤:
-
白名单校验(首选):如果允许访问的文件数量有限,使用白名单(如枚举允许的ID、文件名或路径片段),用户只能传入ID,后端映射到固定路径。
# 危险做法 file = open("downloads/" + user_input, "r") # 安全做法(白名单映射) allowed_files = { "1": "report_q1.pdf", "2": "report_q2.pdf" } file_name = allowed_files.get(user_input) if file_name: file = open("downloads/" + file_name, "r") -
拒绝危险字符:过滤或拒绝包含 、、
%2e%2e%2f(URL编码)或\0(空字节,用于绕过旧版语言扩展名检查)的输入。 -
文件类型与后缀限制:明确允许的后缀列表(如
.pdf,.txt,.png),拒绝其他所有后缀。
路径规范化与安全检查(Canonicalization)
即使过滤了 ,攻击者可能使用长路径、绝对路径或符号链接进行绕过,必须对处理后的最终路径进行校验:
-
解析真实路径:使用系统调用获取路径的“绝对、无符号链接”的真实路径(如
realpath(),或 Python 的os.path.realpath())。 -
限定基目录:检查解析后的真实路径是否以你期望的基目录开头。
import os BASE_DIR = "/var/www/app/data" def safe_file_path(user_input): # 1. 构建安全路径(假设已过滤了危险字符) safe_input = os.path.basename(user_input) # 只取文件名,去掉目录部分 full_path = os.path.join(BASE_DIR, safe_input) # 2. 规范化路径 real_path = os.path.realpath(full_path) # 3. 检查是否在基目录内 if real_path.startswith(os.path.realpath(BASE_DIR)): return real_path else: return None
使用安全的API库
尽量避免直接操作文件系统,使用专为安全设计的库:
- Java:使用
java.io.File的getCanonicalPath()方法并检查前缀,或使用 Spring 的ResourceUtils和UrlResource。 - Node.js:避免使用
fs.readFileSync(userInput),结合path.resolve()并检查是否在项目根目录下。 - .NET:使用
Path.GetFullPath()并配合Directory.GetCurrentDirectory()验证。
部署环境配置(多层防御)
即使代码存在疏忽,以下配置可以显著增加攻击难度:
- 最小权限原则:Web应用程序的运行用户(如
www-data,nginx)只应拥有读取必要目录(如public/,uploads/)的权限,禁止访问系统目录(/etc/,/proc/,/var/log/)。 - 禁用不必要的文件系统功能:在 Web 服务器(Nginx/Apache)配置中,禁止对
uploads目录执行脚本,仅允许静态文件服务。 - 使用隔离的存储服务:如果条件允许,不要将用户文件存储在本地磁盘,使用对象存储(AWS S3, MinIO)或单独的HTTP文件服务,这样,应用程序完全不需要直接操作本地文件系统路径。
常见绕过手法与对应防御
| 攻击手法 | 示例输入 | 防御策略 |
|---|---|---|
| 简单目录遍历 | ../../../etc/passwd |
过滤 |
| URL编码 | ..%252f..%252f..%252f(双重编码) |
解码后再进行路径检测 |
| 16位Unicode编码 | ..%c0%ae%c0%ae/(针对旧版IIS) |
使用最新版本的运行时环境 |
| 绝对路径 | /etc/passwd |
检查路径是否以基目录开头(而非仅检查相对路径) |
| 空字节注入 | ../../../etc/passwd%00.png |
使用强类型语言,避免使用C风格的字符串;对输入进行空字节截断检测 |
| 符号链接 | /var/www/app/data/link_to_etc(用户上传一个符号链接文件) |
使用 realpath() 解析最终路径 |
规避路径遍历漏洞最有效的方法是 “不信任用户输入,永远不要直接拼接路径”,具体落地上:
- 首选非路径方案:使用 ID、哈希等间接引用代替路径。
- 其次白名单:穷举允许访问的文件。
- 最后防御层:使用
realpath()+ 前缀检查,并降低服务器运行权限。 - 持续测试:使用静态代码扫描工具(SAST)和渗透测试工具(如 Burp Suite)定期检测。