
1. 项目背景业务场景本地生活电商上线 6 个月运行平稳——直到某天凌晨 3 点运维被告警电话吵醒。一个外包开发离职前把自己的 MongoDB 账号分享到了公共 GitHub 仓库黑客用这个账号在凌晨删除了 3 个核心集合还在一个集合里留下了勒索信息支付 0.5 BTC 到以下地址否则数据永久丢失。更可气的是安全审查发现所有服务共用一个 root 账号没有区分读写权限开发环境的 MongoDB 端口 27017 直接暴露在公网上没有任何防火墙TLS 连接加密从未开启——数据传输都是明文。痛点MongoDB 默认安装下没有任何安全机制不配置等于裸奔。常见安全隐患使用 root 账号做所有操作——一个应用挂掉误写了 admin 库整个数据库集群权限被破坏所有团队共用密码、明码写在配置文件中不区分开发/测试/监控账号监控系统用的也是 root数据库端口没有 IP 白名单任何人都能尝试连接从 2017 年起有多起大规模的 MongoDB 勒索攻击就是利用未认证的 MongoDB 实例直接删除数据并勒索。2. 项目设计剧本式交锋对话小胖脸色苍白地看新闻大师我刚看到一篇报道——一家公司 MongoDB 没设密码被黑客删库勒索了 0.5 个比特币我们的 MongoDB 是不是也很危险大师你们目前用的是什么账号小胖呃……admin / admin123。大师叹气那就是裸奔。MongoDB 安全有三个基本防线——认证你是谁、授权你能做什么、加密你的数据在路上安不安全。目前你们三道防线全没守住。小胖认证和授权有区别吗登录不就是确定权限吗大师是两步。“认证是 MongoDB 回答你是谁”通过用户名密码或证书完成“授权是回答你能做什么”通过角色Role来管理。认证成功后你的操作被分配给角色的权限框架校验。技术映射MongoDB 的认证默认使用 SCRAMSalted Challenge Response Authentication Mechanism密码不会以明文形式在网络上传送。授权基于 RBACRole-Based Access Control每个用户被授予一个或多个角色角色定义了哪些操作可以在哪些数据库/集合上执行。小白我看到 MongoDB 有很多内置角色——read、readWrite、dbAdmin、clusterMonitor、root……这些怎么区分大师画张表最清楚角色权限范围适用对象read仅读取指定数据库只读报表、数据分析师readWrite读写指定数据库业务应用服务dbAdmin管理数据库索引、统计、清理DBA不含读写数据userAdmin管理用户和角色安全管理员clusterAdmin管理集群复制集、分片运维/DBAclusterMonitor查看集群状态无写权限Prometheus 监控backup/restore备份和恢复备份系统专用账号root所有权限仅超级管理员禁止日常使用小胖那最小权限原则就是——开发用 readWrite监控用 clusterMonitor备份用 backup日常操作绝不给 root大师一针见血。还有一个技巧账号分层。每一层开发环境、测试环境、预发布、生产环境用不同的用户名和密码不要复用。开发环境的密码泄露不会影响生产环境。技术映射db.createUser()创建用户db.grantRolesToUser()追加角色db.revokeRolesFromUser()撤销角色。第 3 章讲过的authenticationDatabase就是指用户被定义在哪个库——root 和系统级用户在 admin 库业务用户在各自业务库。小白那 TLS/SSL 连接加密呢是不是配个证书就行了大师原理跟你 HTTPS 访问网站一样——客户端和服务端各自持有证书建立加密通道。MongoDB 的 TLS 可以双向认证客户端也需证书但多数项目只做单向服务端出示证书。配置 TLS 的三个要点需要一个有效的 CA 签发的证书内网可用自签 CA。服务端证书的 CN 或 SAN 必须与连接串中的主机名匹配。启用 TLS 后端口不变27017但传输内容加密。小胖还有个问题——MongoDB 偶尔曝出严重漏洞比如 CVE我们怎么知道什么时候该打补丁大师这属于安全运维的持续过程。建议订阅 MongoDB 的安全公告邮件生产环境用 MongoDB 的稳定版本偶数版本号如 7.0、8.0避开刚出的 .0 版本Docker 部署的好处是可以快速滚动升级镜像版本而不停服务配合复制集。大师总结今天的三件事——建账号分层体系给每个服务/角色独立的用户名和最小权限如果只是在内网跑至少也要开 SCRAM 认证如果数据是用户的 PII个人信息TLS 加密是底线。明天开始先把 admin123 改掉。3. 项目实战3.1 环境准备沿用第 2 章的 Docker MongoDB它已在docker-compose.yml中配了 root 用户。3.2 分步实现步骤一创建分层账号体系目标为开发、测试、只读报表、监控系统创建独立账号遵循最小权限原则。// 使用 root 账号连接use admin// 1. 创建业务应用账号local_life 库的读写权限db.createUser({user:app_life,pwd:App_Life_Pss_2026,roles:[{role:readWrite,db:local_life}// 仅在 local_life 库有读写]})// 2. 创建只读报表账号db.createUser({user:report_reader,pwd:Report_Read_Only_2026,roles:[{role:read,db:local_life}// 仅可读取]})// 3. 创建测试账号db.createUser({user:tester_life,pwd:Test_Life_2026,roles:[{role:readWrite,db:local_life_test}]})// 4. 创建监控专用账号不读写数据只看集群状态db.createUser({user:monitor,pwd:Monitor_2026,roles:[{role:clusterMonitor,db:admin},// 查看集群指标{role:readAnyDatabase,db:admin}// 可读所有库的元数据可选]})// 5. 创建备份专用账号db.createUser({user:backup_user,pwd:Backup_2026_Secure,roles:[{role:backup,db:admin},{role:restore,db:admin}]})// 查看所有用户db.getUsers()步骤二验证权限边界目标用不同账号连接并尝试越权操作验证权限正确生效。// 场景1报表账号尝试写入 // 用 mongosh 连接 report_reader// mongosh mongodb://report_reader:Report_Read_Only_2026localhost:27017/local_life?authSourceadmin// 尝试读取应成功use local_life db.products_idx.findOne()// → 应该成功返回// 尝试写入应失败db.products_idx.insertOne({name:越权测试})// → 错误: not authorized on local_life to execute command { insert: products_idx ... }// 场景2测试账号尝试读写 local_life 库 // 连接 tester_life// mongosh mongodb://tester_life:Test_Life_2026localhost:27017/local_life?authSourceadminuse local_life db.products_idx.findOne()// → 错误: not authorized on local_life该账号只有 local_life_test 的权限// 场景3监控账号尝试读写数据 // mongosh mongodb://monitor:Monitor_2026localhost:27017/admin?authSourceadminuse local_life db.serverStatus()// 查看服务器状态应成功因为 clusterMonitor 角色有权限db.products_idx.findOne()// → 错误: not authorizedclusterMonitor 没有数据读写权限步骤三密码安全——修改密码与密钥轮换目标学习修改密码、吊销账号、启用密码检查。// 使用 root 账号连接use admin// 修改密码db.changeUserPassword(app_life,App_Life_N3w_Pss_2026Q2)// 验证新密码生效// 用旧密码连接 → 应失败// 用新密码连接 → 应成功// 吊销账号权限保留用户但撤销角色db.revokeRolesFromUser(tester_life,[{role:readWrite,db:local_life_test}])// 完全删除用户db.dropUser(tester_life)// 启用密码复杂度检查MongoDB 企业版// MongoDB 社区版不内置密码策略需在应用层做或使用外部认证// Atlas 云服务默认有密码强度校验步骤四为应用账号授予自定义角色目标创建自定义角色——允许在商品集合上执行特定操作。use local_life// 创建自定义角色——商品管理员db.createRole({role:productManager,privileges:[{resource:{db:local_life,collection:products},actions:[find,insert,update,remove]},{resource:{db:local_life,collection:products},actions:[createIndex,dropIndex]}],roles:[]// 不继承其他角色})// 将自定义角色授予用户db.createUser({user:product_admin,pwd:Product_Admin_2026,roles:[{role:productManager,db:local_life}]})// 验证product_admin 只能操作 products 集合// 连接 product_admin// mongosh mongodb://product_admin:Product_Admin_2026localhost:27017/local_life?authSourcelocal_lifeuse local_life db.products.insertOne({name:权测试})// → 应成功products 集合db.users.findOne()// → 应失败无 users 集合权限步骤五连接加密——自签 TLS 证书配置概述目标了解 MongoDB TLS 加密的配置流程生产环境需正规 CA 证书。# TLS 配置流程以自签证书为例仅在本地测试环境使用# 1. 生成 CA 私钥和证书openssl genrsa-outca.key2048openssl req-x509-new-nodes-keyca.key-sha256-days365-outca.crt\-subj/CNMongoDB-Test-CA# 2. 生成服务端私钥和证书签名请求openssl genrsa-outmongod.key2048openssl req-new-keymongod.key-outmongod.csr\-subj/CNlocalhost# 3. 用 CA 签发服务端证书openssl x509-req-inmongod.csr-CAca.crt-CAkeyca.key\-CAcreateserial-outmongod.crt-days365-sha256# 4. 合并证书和私钥为 PEM 文件catmongod.key mongod.crtmongod.pemchmod600mongod.pem# 5. 启动 mongod 时指定证书# 在 docker-compose.yml 中增加# command: --tlsMode requireTLS --tlsCertificateKeyFile /etc/ssl/mongod.pem --tlsCAFile /etc/ssl/ca.crt# 并将证书文件挂载进容器# 6. 客户端连接时指定 TLS# mongosh --tls --tlsCAFile ca.crt --host localhost注意TLS 配置较复杂且环境的差异大本章仅展示流程框架。生产环境强烈建议使用 MongoDB Atlas云服务自动配 TLS或让运维团队通过 Kubernetes Secret 管理证书。步骤六安全检查清单目标输出一份团队自检用的安全清单。// MongoDB 安全自检脚本管理员视角use admin// 1. 检查是否开启认证constauthStatusdb.runCommand({getCmdLineOpts:1})print(认证状态:,JSON.stringify(authStatus.parsed?.security?.authorization||未开启 ⚠))// 2. 列出所有用户及其角色db.system.users.find({},{user:1,roles:1}).forEach(u{print(用户:${u.user}, 角色:${JSON.stringify(u.roles)})})// 3. 检查 root 用户个数应只有 1 个constrootCountdb.system.users.countDocuments({roles.role:root})print(Root 用户数:,rootCount,rootCount2?⚠ 过多:PASS)// 4. 检查是否有空密码用户constnoPwddb.system.users.find({credentials.SCRAM-SHA-256:{$exists:false}}).count()print(弱认证账号:,noPwd,noPwd0?⚠ 存在:PASS)3.3 完整代码清单文件用途mongodb-lab/scripts/ch13-create-users.js创建分层账号体系的脚本mongodb-lab/scripts/ch13-verify-permissions.js权限验证脚本mongodb-lab/scripts/ch13-custom-role.js自定义角色演示mongodb-lab/scripts/ch13-security-checklist.js安全自检脚本3.4 测试验证// 验证脚本以 root 身份运行use admin// 1. 确认业务账号仅能访问 local_lifeconsttestAuthdb.runCommand({usersInfo:{user:app_life,db:admin},showPrivileges:false})print(app_life 角色:,JSON.stringify(testAuth.users[0].roles))// 应只含 {role:readWrite, db:local_life}// 2. 确认监控账号不能写数据consttestMonitordb.runCommand({usersInfo:{user:monitor,db:admin}})constmonitorRolestestMonitor.users[0].roles.map(rr.role)print(monitor 是否含有 data 角色:,monitorRoles.some(rr.includes(readWrite)||r.includes(root))?FAIL:PASS)// 3. 确认测试账号已删除consttestTesterdb.system.users.findOne({user:tester_life})print(tester_life 是否已清理:,testTesternull?PASS:FAIL)// 4. 清理如需// db.dropUser(app_life)// db.dropUser(report_reader)// db.dropUser(monitor)// db.dropUser(backup_user)// db.dropUser(product_admin)// db.runCommand({ dropRole: productManager })print(\n 安全性验证完成 )4. 项目总结4.1 MongoDB 安全 vs MySQL 安全维度MongoDBMySQL说明认证机制SCRAM-SHA-256默认mysql_native_password / caching_sha2_password两者均支持强认证角色管理内置角色 自定义角色全局权限 库级权限MongoDB 角色粒度更灵活网络加密TLS配置稍复杂TLS功能等价默认安装安全无认证4.0 前有初始密码5.7MySQL 近年改进更明显审计日志企业版功能企业版 插件社区版两者均无内置审计字段级加密客户端字段级加密CSFLE需应用层实现MongoDB 有原生方案4.2 适用场景MongoDB 安全配置适用生产环境所有 MongoDB 实例——必须开启认证和最小权限。需要多团队协作的项目——不同账号不同权限互不干扰。数据合规要求GDPR/PIPL——传输加密 字段级加密 审计。监控和备份系统的账号隔离——专用账号避免权限泄漏。CI/CD 管道中的临时数据库——动态创建账号并在管道结束时销毁。不适用场景纯本机单机学习环境——开认证增加心智负担但关闭网络端口。只读的数据副本——如果不需要区分用户可以用--auth但配简单密码。4.3 注意事项注意事项说明认证库authSourceroot 角色在 admin 库业务用户在对应业务库连接时必须正确指定密码明文配置文件中勿写明文密码用环境变量或 Secret ManagerVault/AWS Secrets Manager匿名角色readAnyDatabase小心授予——它允许读所有数据库含 system 集合未加密的公网 MongoDB从 2017 年起已有数万起勒索攻击案例永久不要公网裸奔TLS 证书过期自签证书有效期设为定期提醒项过期会导致所有连接中断4.4 常见踩坑经验故障案例一authSource 配错导致连接失败新来的运维在 Connection String 中写了mongodb://app_life:pwdhost:27017/local_life没加?authSourceadmin结果连接报错 “Authentication failed”。根因app_life是在 admin 库中创建的root/admin 账号都是而连接串默认以目标数据库local_life作为认证库。解决连接串末尾加?authSourceadmin或在local_life库中直接创建app_life用户。故障案例二监控账号权限不足导致 Prometheus 数据缺失团队创建了monitor用户并授予clusterMonitor但 Prometheus 的mongodb_exporter请求db.serverStatus()正常请求db.stats()却报 403。根因clusterMonitor允许serverStatus但不允许dbStats这是数据库级别的操作。解决追加{ role: read, db: local_life }到 monitor 用户——有 read 权限就能执行dbStats。故障案例三密码泄露后未及时吊销导致数据外泄某项目 GitHub 仓库中误提交了含 MongoDB 密码的application.yml3 天后才发现。此时黑客已通过该账号全量导出了用户数据。根因密码泄露后的响应太慢且账号有全量readWrite权限。解决事后应对——立即修改密码 检查审计日志事前预防——使用 GitHub Secret Scanning .gitignore明文配置文件 密码存入环境变量。4.5 思考题如果一个用户被授予了readWrite在 database_A又被授予了read在 database_B他能跨库做$lookup从 database_A 关联 database_B 的数据吗MongoDB 的客户端字段级加密CSFLE是如何实现数据库管理员也看不到明文的加密和解密在哪里发生答案将在第 14 章末尾揭晓上一章思考题答案Spring Data MongoDB 的分页PageProduct底层使用的是skip limit而非游标分页。它在构造Query时自动加入.skip(page * size).limit(size)不涉及游标编码/解码。这也是为什么用 Spring Data 默认分页翻到深页性能会急剧下降——skip 机制的本质问题无法绕过。若需高性能深分页必须手动实现游标分页用 MongoTemplate 构造$gt/$lt条件。Spring Boot 的读写分离配置在MongoClientSettings中设置readPreference(ReadPreference.secondary())。一致性陷阱——写后立刻读可能从 Secondary 读不到刚写的数据未复制完成Secondary 延迟较大时可能读到过期数据。对策要求强一致性的读操作在MongoTemplate查询时临时覆写readPreference为primary但会导致读写分离失效该查询的流量回到主库。延伸阅读与资源python入门Rquests从菜鸟脚本到企业级SDK的网络实战圣经Milvus向量数据库实战修炼从 0 到 1精通向量检索与生产落地后端工程师的 AI 转型第一课Ollama 与私有化大模型实战10倍开发者的 Dify 魔法书从零构建全栈 AI 应用后端工程师转型AI第一课-Ollama 与私有化大模型实战大型语言模型(LLM) vLLM 高性能推理落地实战Agent开发之LlamaIndex 实战修炼与源码进阶大语言模型Transformers 实战修炼与源码剖析