附件下载接口把 file 参数拼到目录后面,请求 ../../../../etc/passwd 返回 200,响应头是 application/octet-stream,正文第一行是 root:x:0:0。日志里能对上时间点,这种反而好排。
参数原样参与路径拼接,两个点加斜杠被操作系统当成上跳目录。只过滤 ../ 也不够,URL 编码一次、反斜杠、绝对路径都能绕过去。校验发生在拼接之前,等于没校验。这一点先确认清楚,后面就不用反复试。
base = os.path.realpath('/data/files')
target = os.path.realpath(os.path.join(base, name))
if not target.startswith(base + os.sep):
raise ValueError('bad path')
「下载接口路径穿越」改完先别急着全量,挑一台机器放半天,真有问题这段时间就能看出来。先做最小复现,能稳定跑出来的问题,排查成本会低一个数量级。
别只看报错的最后一行,往上看几行,真正的起因通常在上面。
处理上按这个顺序:拼完路径用 realpath 规范化 再判断是否还在基准目录内;文件名只保留白名单字符 去掉斜杠和两个点;附件不按用户传的名字找 改成数据库里的 id 反查真实路径;对 URL 编码和反斜杠各造一条用例 确认都返回 400。
结果对不对很好判断,再请求 ../../etc/passwd 返回 400 正常文件名照旧能下 响应长度不变。
