不能只靠应用层校验,因为攻击者可直连数据库绕过代码逻辑;必须结合mongodb 4.4+行级rbac(如privilegedocument过滤)与应用层双重校验,且字段路径、操作符须完全一致。

为什么不能只靠应用层校验
只在代码里做 if (user.id === file.metadata.ownerId) 判断,挡不住直连数据库的攻击。攻击者用 mongosh 登录后执行 db.fs.files.find() 就能拿到所有文件 _id,再用 GridFSBucket.openDownloadStream(new ObjectId("...")) 直接下载——应用层根本没机会介入。
常见错误是给用户分配了 readWrite 角色但没限定集合范围,结果 fs.chunks 也被开放。一旦 chunks 集合可读,攻击者就能用 db.fs.chunks.find() 批量拉取全部二进制块,拼出任意文件。
- RBA C 必须禁用对
fs.chunks的所有权限(find、insert、delete) - 所有权限逻辑必须收敛到
fs.files集合,且只允许通过metadata.access_control字段过滤 - 超级管理员账号绝不能复用在应用连接池中,否则 RBAC 形同虚设
如何配置带行级过滤的 RBAC 角色
MongoDB 4.4+ 支持在 privilegeDocument 中写查询条件,这是服务器端兜底的关键。错误做法是只授权 { resource: { db: "mydb", collection: "fs.files" }, actions: ["find"] },这样用户能扫出全部文件 ID。
正确写法需显式声明过滤条件,例如限制用户只能查自己有权限的文件:
{ resource: { db:"mydb", collection:"fs.files"}, actions: ["find"], privilegeDocument: { "metadata.access_control.user_ids": {"$in": ["$$USER_ID"] } } }
注意:$$USER_ID 是 MongoDB 内置变量,需配合 authenticationRestrictions 或会话上下文使用;若用角色继承,确保基础角色已绑定用户标识字段。
- 对角色组权限(如
roles: ["editor"]),要用"metadata.access_control.roles": { "$in": ["$$ROLES"] },不能写成"metadata.access_control.roles": "editor" -
privilegeDocument查询必须匹配索引结构,否则仍会全表扫描 - 避免在
metadata里存敏感信息,RBAC 不加密字段内容
应用层下载前必须做的两件事
即使 RBAC 已设行级过滤,应用层仍不能跳过校验直接开流。因为 RBAC 只拦数据库直连,不拦业务逻辑绕过——比如前端传来的 fileId 可能是伪造的,或用户已离职但 token 未失效。
调用 GridFSBucket.openDownloadStream() 前,必须先查 fs.files 文档并校验权限:
- 用
bucket.find({ filename: "a.pdf", "metadata.access_control.user_ids": "u123" })查存在性与权限,不是仅查_id - 查询必须命中复合索引,例如
db.fs.files.createIndex({ filename: 1, "metadata.access_control.user_ids": 1 }) - 上传时强制写入
metadata.access_control,至少含user_ids、roles、is_public三个键,别用单值owner_id
预览接口为何不能暴露 _id
HTTP 接口若直接接收 /file/507f1f77bcf86cd799439011 这类路径参数,就等于把文件句柄白送给任何人。GridFS 没内置鉴权,_id 一旦泄露,攻击者构造请求即可绕过所有中间件。
安全做法是用 Token 校验代替路径 ID:
- 中间件解析 JWT 后,从
scope或roles字段提取权限,而非只校验签名有效性 - 用
req.headers.authorization?.split(' ') || []提取 token,避免Bearer undefined导致 500 - 流式返回时监听
stream.on('error', ...),否则 MongoDB 连接断开会触发 unhandledRejection - 设置
Content-Disposition: inline控制浏览器内嵌显示,否则 Chrome 等可能强制下载
最易被忽略的是:RBAC 行级过滤和应用层查询必须用完全一致的字段路径和操作符,否则任一环节失效,整个权限体系就塌一角。











