结论:路径穿越指应用把用户输入拼进文件路径而未过滤 ../,攻击者用 ../../etc/passwd 跳出预期目录读写任意文件。防御核心不是过滤 ..(编码变体太多),而是解析后校验:规范化(realpath/canonical path)后的绝对路径必须仍位于允许的根目录之内,或干脆不让用户输入成为路径(id → 映射表查路径)。

展开:绕过花样说明为何黑名单必败:URL 双重编码 %252e%252e%252f、Unicode/全角变体、Windows 的 ..\、结尾加 %00 截断(老系统)、zip slip(解压恶意压缩包里的 ../../evil.sh 条目——解压库逐条落地前必须检查目标路径)。正确防御姿势:1)首选间接引用——下载接口收文件 ID,服务端查表得真实路径,用户输入根本不参与路径构造;2)必须拼接时:base.join(user).normalize().startsWith(base)(Java)或 Python os.path.realpath 后比较前缀(注意 Path('/srv/files').resolve() / user 中 user 以 / 开头会直接替换 base 的坑);3)OS 层加固——运行账户最小权限、chroot/容器只读文件系统限制爆炸半径。易错点:startsWith('/srv/files') 字符串比较被 /srv/files-secret 绕过,比较要在路径分段级别;TOCTOU——检查后文件被换成符号链接,高安全场景用 openat2 RESOLVE_BENEATH

from pathlib import Path
base = Path("/srv/files").resolve()
target = (base / user_input).resolve()
if not target.is_relative_to(base):   # 3.9+
    raise ValueError("非法路径")

追问方向:zip slip 在各大解压库的历史 CVE、符号链接攻击与 hardening(fs.protected_symlinks)。