必须在上传入口层做四层校验:文件名、类型、元数据、大小,缺一不可;filename限255字符且禁路径遍历符,contentType须白名单,magic bytes校验防扩展名欺骗,metadata防深层嵌套与非法键名,预读首1MB校验大小并前置拦截。

必须在上传入口层做四层校验:文件名、类型、元数据、大小,缺一不可;否则攻击者几行脚本就能打爆磁盘或注入非法内容。
filename 必须限制长度并过滤路径遍历字符
MongoDB 对 filename 字段完全不校验,攻击者可传 ../../../../etc/passwd 或 10MB 随机字符串触发集合膨胀。业务侧必须拦截:
-
filename总长 ≤ 255 字符(BSON key 长度上限 + 实际可读性) - 禁止出现
/、../、\0、Unicode 零宽空格(\u200b)等路径遍历或混淆符号 - 建议用正则
^[a-zA-Z0-9._-]+$白名单过滤,而非黑名单剔除
contentType 必须白名单强制校验
客户端传的 Content-Type header 或 metadata.contentType 可被任意伪造,仅靠它判断类型等于开门揖盗:
- 只允许已知安全类型,例如:
"image/jpeg"、"application/pdf"、"text/plain" - 禁用通配符如
"image/*"或模糊值如"application/octet-stream" - 对上传的二进制流前 1KB 做 magic bytes 校验(如 PDF 必须以
%PDF-开头),防止扩展名欺骗
metadata 深度与键名必须递归检查
开放 metadata 写入时,攻击者可通过深层嵌套或非法键名触发 BSON 解析异常或内存耗尽:
- 递归检查嵌套层级,深度 > 5 层直接拒绝
- 禁止键名含
.或$(MongoDB 查询操作符前缀,可能被用于 $where 注入) - 键名长度单个 ≤ 128 字符,总键数 ≤ 64 个(防超大文档)
- 值类型限定为 string/number/boolean/ObjectId,拒绝 Function、RegExp、undefined
上传前必须预读首 1MB 并校验 Content-Length
Content-Length header 可被伪造,只靠它限流会失效;而全量读入内存再校验又会导致 OOM:
- 用
fs.createReadStream()(Node)或stream.read(1024*1024)(Python)预读首 1MB - 对比预读字节数与
Content-Length,偏差 > 5% 视为可疑,直接 413 - 设置硬阈值(如 100MB),超过即中断流、关闭 socket,不进入
uploadFromStream() - Express 用户务必禁用
bodyParser对 multipart 的自动解析,它默认缓存整个 body 到内存
最易被忽略的是:校验逻辑必须在 uploadFromStream() 调用之前完成——一旦流开始写入,哪怕后续校验失败,fs.files 文档已落库,fs.chunks 可能已写入部分块,清理成本远高于前置拦截。


















