GridFS本身不支持客户端加密,需自行封装加解密逻辑:上传前用AES-GCM等认证加密算法加密数据并存IV、authTag等到metadata,下载时提取参数解密;密钥须由客户端生成保管,严禁存服务端。

GridFS 本身不支持客户端加密,必须自己封装加解密逻辑
GridFS 只是把大文件切块存进 MongoDB 的 fs.files 和 fs.chunks 集合,它不碰文件内容,更不会帮你加密。所谓“客户端加密”,实际是你在调用 GridFSBucket.openUploadStream() 前,把原始数据流先用密钥加密,再喂给 GridFS;读取时则反向操作:从 GridFS 读出密文流 → 解密 → 得到明文。
加密必须在上传前完成,且密钥不能存在服务端数据库里
常见错误是把密钥硬编码在 Node.js 后端代码里,或者存进 MongoDB —— 这等于没加密。真正安全的做法是:密钥由客户端生成并保管(比如 Web Crypto API 生成的 importKey() 结果),只传加密后的密文和必要的元信息(如 IV、算法标识)进 GridFS。
- 上传时:用
AES-GCM加密原始 Buffer,把iv、authTag作为 metadata 存入uploadStream的metadata字段 - 下载时:先读
downloadStream得到密文流,再从file.metadata.iv等字段提取参数,最后用客户端持有的密钥解密 - 避免用
CBC模式:它需要 padding 且不带认证,容易被篡改;GCM或ChaCha20-Poly1305更合适
注意流式加密与 GridFS 分块的错位风险
GridFS 默认按 chunkSizeBytes: 255 * 1024(255KB)切块,但加密算法处理的是连续字节流。如果直接把未加密文件流 pipe 给加密流,再 pipe 给 uploadStream,可能因背压或缓冲导致 IV/Tag 错位,解密失败。
- 解决方案:不要依赖自动流式加密,改用
transform stream显式控制分块边界 - 更稳妥做法:先完整加密整个文件(适合小文件 ≤ 50MB),再用
Buffer创建可读流传给openUploadStream() - 大文件场景必须流式处理时,确保加密 Transform Stream 的
_transform方法每次只处理完整加密块(例如 GCM 的 16KB 对齐),并手动管理 IV 传递
metadata 字段要存够解密所需的全部非密钥参数
解密失败最常见的原因是丢参数。IV、authTag、算法名、密钥派生参数(如 salt、iterations)都得存,且类型要一致。MongoDB 的 metadata 是 BSON 对象,支持 BinData,但 Web Crypto 输出的 ArrayBuffer 需转成 Uint8Array 再用 new Binary(...) 包装。
- 必须存:
encryptionAlgorithm(如"AES-GCM")、iv(Binary类型)、authTag(Binary类型) - 建议存:
keyDerivation(含salt和iterations),方便未来换密钥策略 - 别存:
password、rawKey、privateKeyPem—— 这些永远不该出现在服务端上下文里
加密不是加个 wrapper 就完事,最麻烦的是密钥生命周期管理和加解密上下文一致性。一旦客户端换设备、重装 App 或丢失密钥,文件就永久不可读 —— 这个代价得提前想清楚。

















