Skip to content

未开放任何权限(含创建目录和上传)的guest用户可以直接非法创建目录/上传文件 #9600

Closed

Description

@tqluffy

Please make sure of the following things

  • I have read the documentation.
    我已经阅读了文档。

  • I'm sure there are no duplicate issues or discussions.
    我确定没有重复的issue或讨论。

  • I'm sure it's due to AList and not something else(such as Network ,Dependencies or Operational).
    我确定是AList的问题,而不是其他原因(例如网络,依赖或操作)。

  • I'm sure this issue is not fixed in the latest version.
    我确定这个问题在最新版本中没有被修复。

AList Version / AList 版本

v3.62.0

Driver used / 使用的存储驱动

ilanzou

Describe the bug / 问题描述

alist版本号为v3.62.0,部署在阿里云ubuntu24,通过cloudflare tunnel将端口映射到netdisk.zhtianqi.cn。
guest用户配置项:{"密码":"无";"基本路径":"/public";"权限":"无"}。
"/public"挂载为蓝奏云优享版的"我的文件/public"。
直接在文件夹/文件右键确实只有分享/下载/复制链接,但是点击右下角的更多时可以直接通过那里的工具进行创建文件夹/创建文件/上传文件,guest无任何权限应当只能访问并下载,不应该能执行写入,疑似重大bug。

Reproduction / 复现链接

netdisk.zhtianqi.cn

Config / 配置

config.json

Logs / 日志

log.log

Activity

  1. okatu-loli commented on Aug 4, 2026

    @okatu-loli
    Collaborator

    已排查完成,你遇到的写入不是权限绕过,而是元信息(meta)的「写入」开关在起作用;但排查过程中发现了另一个真实的安全回归,已单独修复。

    一、你的 guest 能上传的原因

    AList 的写入判定是「角色权限位 或 元信息写入开关」二选一(server/middlewares/fsup.go):

    common.HasPermission(perm, common.PermWrite) || common.CanWrite(meta, stdpath.Dir(path))

    你的实例上:

    • guest 角色权限是 {path: /public, permission: 2},bit1 是「免密访问」,不含写入位 — 这部分是对的;
    • 但 /public 的元信息开启了「写入」。证据:guest 身份请求 /api/fs/list 返回 "write": true,而该字段的来源正是 HasPermission(perm, PermWrite) || CanWrite(meta, reqPath);
    • 我用一个不产生任何写入的探针确认了后端确实放行(对已存在路径带 Overwrite: false,权限若通过会在写入前短路):
    PUT /api/fs/put  File-Path=/public/ilanzou  Overwrite=false  →  {"code":403,"message":"file exists"}   ← 权限已通过
    PUT /api/fs/put  File-Path=/ilanzou         Overwrite=false  →  {"code":403,"message":"permission denied"}
    

    解决办法:管理后台 → 元信息 → /public → 关闭「写入」(以及「写入应用到子目录」)。前端右下角工具栏正是依据接口返回的 write 字段显示的,关闭后按钮也会消失。

    元信息的写入开关设计上就是「不给账号权限也能让访客上传」的匿名投递功能,优先级高于角色权限位,配置时容易误开,请检查一下是不是设置 readme/header 时一并打开了。

    二、排查中发现的真实 Bug(已修:#9601)

    utils.JoinBasePath 中有一个提前返回,导致用户的「基本路径」从未生效:

    reqPath = FixAndCleanPath(reqPath)
    if strings.HasPrefix(reqPath, "/") {
        return reqPath, nil     // FixAndCleanPath 一定补前导 /,所以这里永远命中
    }

    后果:被基本路径限制的用户实际没有被限制住。内置 guest/general 角色的权限作用域是 /,而 CanReadPathByRole 对作用域下的任意路径放行 — 所以一个限制在 /public 的 guest,只要直接请求其他存储的绝对路径就能列目录和下载。本地复现(guest,基本路径 /lanzou,无写权限):

    POST /api/fs/list {"path":"/wukong"}  →  200,返回了另一个存储的完整内容
    

    这同时解释了你实例上的另一个现象:guest 点进子文件夹会报 403 — 因为 /ilanzou 没有被拼接成 /public/ilanzou,而是打到了真实根目录。修复后这个问题也会一并解决。

    回归引入自 6b2d81e (#9249),修复 PR:#9601。

    请注意:在 #9601 合并发布前,你的实例上任何靠「基本路径」做隔离的用户都是不可靠的,建议临时改用角色权限作用域(把 guest 角色的 scope 限定为 /public,你已经是这样配置的,所以你的实例目前没有越权读取风险)。

  2. okatu-loli commented on Aug 6, 2026

    @okatu-loli
    Collaborator

    已修复,请更新3.63.0版本

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

    bugSomething isn't working

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions