GridFS上传必须校验元信息、使用GridFSBucket、绑定身份配额、预读校验大小、定期清理孤儿块;filename限255字符且禁路径遍历符,contentType须白名单,metadata需防深层嵌套与非法键名。

GridFS 上传前必须校验文件元信息
不校验 filename、contentType 和 metadata,攻击者就能用随机字符串或超长路径名反复写入,撑爆 fs.files 集合。MongoDB 本身不限制文件名长度或格式,但业务侧必须拦住明显异常的值。
-
filename应限制在 255 字符内,且禁止/、../、控制字符、Unicode 零宽空格等路径遍历或混淆符号 -
contentType必须白名单校验,例如只允许"image/jpeg"、"application/pdf"等已知合法类型,不能只靠客户端传入的值 -
metadata字段若开放写入,需递归检查嵌套层级(避免深度 > 5)和键名(禁止$开头或.出现在键中)
用 GridFSBucket 而非原始 collection 操作
直接往 fs.files 和 fs.chunks 插文档等于绕过所有安全钩子,连基本的 chunk 大小校验都失效。官方 GridFSBucket 类封装了原子写入、分块校验和自动 cleanup,是唯一可控入口。
- 永远不要用
db.fs.files.insertOne()手动插入元数据文档 - 上传必须走
bucket.uploadFromStream(),并设置chunkSizeBytes(建议 256KB,太小增加 chunk 数量,太大拖慢单次写入) - 启用
disableMD5(默认 true),避免 MD5 计算成为 DoS 攻击面
上传接口必须绑定身份与配额
GridFS 本身无权限模型,全靠上层服务控制。一个未鉴权或弱鉴权的上传端点,几行脚本就能打满磁盘。
- 每个上传请求必须关联明确用户 ID 或 API Key,并在 DB 中维护该主体的
daily_upload_count和total_size_bytes - 上传前查 Redis 缓存做秒级限流(如
INCR user:123:upload:20240520+EXPIRE),比查 MongoDB 快一个数量级 - 文件大小必须在内存中预读前 1MB 校验(不是只看
Content-Lengthheader,它可被伪造),超过阈值(如 100MB)直接 413
定期清理 orphaned chunks 是硬性运维动作
上传中断、客户端崩溃或代码 bug 都会导致 fs.chunks 里残留没有对应 fs.files 文档的块——这些“孤儿块”不会被自动回收,积少成多直接吃光磁盘空间。
- 每周跑一次
db.runCommand({ cleanUpOrphaned: "fs.chunks" })(MongoDB 4.2+) - 低版本需手写脚本:查出
fs.chunks.files_id不在fs.files._id中的所有 chunk,再批量删除 - 清理前务必备份,命令执行期间会阻塞写入,安排在业务低峰期
最麻烦的不是配置,而是把「上传校验」和「配额扣减」做成原子操作——很多人用两个独立 DB 请求实现,中间失败就会导致配额没扣但文件已存,或者反之。这得靠事务或 Lua 脚本兜底,别图省事跳过去。










