应使用 os.path.realpath 获取真实绝对路径后,用 startswith() 检查是否在授权目录内;需防范符号链接绕过,禁止用户创建软链或逐级校验父路径。

用 os.path.realpath 检查路径是否落在授权目录内
用户传入的路径(比如通过 URL 参数、表单或文件名字段)若直接拼接到 os.path.join(BASE_DIR, user_input),攻击者可提交 ../../etc/passwd 绕过限制。关键不是“过滤点点斜杠”,而是验证最终路径是否真在你允许的范围内。
实操建议:
- 先用
os.path.join拼接,再立刻调用os.path.realpath获取真实绝对路径 - 用
.startswith()判断该真实路径是否以你的白名单根目录(如/var/www/uploads)开头 - 必须在打开文件前做这一步检查,不能只靠正则替换或黑名单过滤
示例:
import os
BASE_DIR = "/var/www/uploads"
user_path = "../../../../etc/passwd"
full_path = os.path.join(BASE_DIR, user_path)
real_path = os.path.realpath(full_path)
if not real_path.startswith(os.path.realpath(BASE_DIR)):
raise PermissionError("Access denied: path outside allowed directory")
# ✅ 此时 real_path 是 "/etc/passwd",不以 "/var/www/uploads" 开头,拒绝
避免用 os.path.join 拼接不可信字符串
os.path.join 对含 .. 的输入不作清理,它只是按平台规则连接路径段。传入 "a", "../b" 会得到 "a/../b",后续若没做 realpath 就直接访问,就中招了。
实操建议:
- 对用户输入先用
os.path.normpath归一化(但注意:它不解决符号链接绕过,仅是初步处理) - 更稳妥的做法是:用
pathlib.Path构造后调用.resolve()(Python 3.6+),它等价于realpath且更直观 - 永远不要假设用户输入“看起来合法”——
"foo/./../bar.txt"和"foo/%2e%2e/bar.txt"(URL 编码后解码)都可能绕过简单字符串匹配
Web 框架中处理静态文件服务时的常见疏漏
Django 的 serve 视图、Flask 的 send_from_directory、FastAPI 的 FileResponse 虽内置防护,但若手动用 open() 或 Path.read_text() 读取文件,防护就失效了。
实操建议:
- Django:优先用
django.views.static.serve并确保document_root是绝对路径且已校验 - Flask:严格使用
send_from_directory(directory, filename),不要写成send_file(os.path.join(directory, filename)) - 自定义视图中,若必须读文件,请把校验逻辑封装成独立函数,每次调用前强制过一遍
realpath + startswith
注意符号链接导致的 realpath 绕过风险
os.path.realpath 会跟随符号链接,如果攻击者在你授权目录里创建了指向系统敏感路径的软链(如 uploads/link → /etc),再请求 link/shadow,realpath 会返回 /etc/shadow,而你的 startswith(BASE_DIR) 判断就失效了。
实操建议:
- 生产环境禁止用户上传或创建符号链接(如设置
tarfile.TarFile.extractall(..., numeric_owner=True, filter="data")) - 若需支持符号链接,改用
pathlib.Path.resolve(strict=True)并捕获FileNotFoundError;同时检查路径每一段是否都在白名单内(逐级parent上溯) - 最彻底的方案:用
os.open(..., flags=os.O_PATH | os.O_NOFOLLOW)(Linux)配合os.stat()避免跟随,但跨平台成本高,一般场景用权限隔离更实际
真正难防的不是 ..,是当路径解析与权限校验不在同一抽象层级时产生的语义差。哪怕用了 realpath,如果校验发生在 symlink 创建之后、文件打开之前,中间窗口仍可能被利用。
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!











