Skip to content

[BUG] /system/download-fail-info 的 file 参数未做归属校验:可读取上传目录外文件,并可用 404/400 差异探测任意路径 #1454

Closed

Description

@borealisalastor-byte

SQLBot Version
最新 main(核实于 f10255b)

Run Mode
源码运行;接口为 POST /system/download-fail-info,不需要数据库即可复现。

Describe the bug

backend/apps/settings/api/base.py 直接把请求体里的 file 拼到 settings.EXCEL_PATH 上,先判断存在性、后判断后缀,且没有任何路径归属校验:

filename = req.file
file_path = os.path.join(path, filename)

if not os.path.exists(file_path):
    raise HTTPException(404, "File Not Exists")

if not filename.endswith('_error.xlsx'):
    raise HTTPException(400, "Only support _error.xlsx")

filename = os.path.basename(file_path)
return FileResponse(path=file_path, filename=filename, media_type=...)

由此有两个问题:

  1. 可以读取上传目录之外的文件:只要名字以 _error.xlsx 结尾,../、绝对路径等取值都会被直接打开并返回(POSIX 主机即可复现;Windows 主机上 ..\..\ 与 C: 驱动器相对路径同样有效);
  2. 存在性探测:存在性检查排在后缀检查之前,因此"路径存在但后缀不合规"返回 400,"路径不存在"返回 404 —— 仅凭状态码就能判断任意路径是否存在,无需通过后缀校验。

To Reproduce

  1. 准备上传目录内的 <EXCEL_PATH>/orders_error.xlsx,以及目录外的 <tmp>/outside/secret_error.xlsx;
  2. POST /system/download-fail-info,body 为 {"file": "../outside/secret_error.xlsx"}:修复前返回 200 与目录外文件内容;
  3. {"file": "missing.xlsx"} → 404,{"file": "orders.xlsx"}(文件存在但后缀不合规)→ 400,两者可区分,可用于探测存在性。

本地实际执行(真实路由挂载到临时应用、上传目录重定向到临时目录):../outside/secret_error.xlsx 与 ./../outside/secret_error.xlsx 都返回 200 与目录外内容;orders.xlsx / missing.xlsx 分别返回 400 / 404;sub_error.xlsx(同名目录)返回 200 而不是拒绝。

Expected behavior

  • file 只接受导出接口生成的单个文件名:带目录分量、盘符或控制字符的取值直接拒绝,不做静默改写;
  • 先完成文件名与后缀校验,再访问文件系统;随后用 os.path.realpath + os.path.commonpath 确认解析结果仍在上传目录内(符号链接一并解析),并且只返回普通文件;
  • 拒绝路径不回显内部绝对路径或暴露存在性。

Additional context

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions