第13章:安全基础——账号、权限与连接加密
2026/7/23 23:26:13 网站建设 项目流程

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 的认证默认使用 SCRAM(Salted Challenge Response Authentication Mechanism),密码不会以明文形式在网络上传送。授权基于 RBAC(Role-Based Access Control),每个用户被授予一个或多个角色,角色定义了哪些操作可以在哪些数据库/集合上执行。

小白:我看到 MongoDB 有很多内置角色——read、readWrite、dbAdmin、clusterMonitor、root……这些怎么区分?

大师:画张表最清楚:

角色权限范围适用对象
read仅读取指定数据库只读报表、数据分析师
readWrite读写指定数据库业务应用服务
dbAdmin管理数据库(索引、统计、清理)DBA(不含读写数据)
userAdmin管理用户和角色安全管理员
clusterAdmin管理集群(复制集、分片)运维/DBA
clusterMonitor查看集群状态(无写权限)Prometheus 监控
backup/restore备份和恢复备份系统专用账号
root所有权限仅超级管理员,禁止日常使用

小胖:那最小权限原则就是——开发用 readWrite,监控用 clusterMonitor,备份用 backup,日常操作绝不给 root?

大师:一针见血。还有一个技巧:账号分层。每一层(开发环境、测试环境、预发布、生产环境)用不同的用户名和密码,不要复用。开发环境的密码泄露不会影响生产环境。

技术映射db.createUser()创建用户,db.grantRolesToUser()追加角色,db.revokeRolesFromUser()撤销角色。第 3 章讲过的authenticationDatabase就是指用户被定义在哪个库——root 和系统级用户在 admin 库,业务用户在各自业务库。

小白:那 TLS/SSL 连接加密呢?是不是配个证书就行了?

大师:原理跟你 HTTPS 访问网站一样——客户端和服务端各自持有证书,建立加密通道。MongoDB 的 TLS 可以双向认证(客户端也需证书),但多数项目只做单向(服务端出示证书)。配置 TLS 的三个要点:

  1. 需要一个有效的 CA 签发的证书(内网可用自签 CA)。
  2. 服务端证书的 CN 或 SAN 必须与连接串中的主机名匹配。
  3. 启用 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_P@ss_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_2026@localhost:27017/local_life?authSource=admin"// 尝试读取(应成功)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_2026@localhost:27017/local_life?authSource=admin"use local_life db.products_idx.findOne()// → 错误: not authorized on local_life(该账号只有 local_life_test 的权限)// == 场景3:监控账号尝试读写数据 ==// mongosh "mongodb://monitor:Monitor_2026@localhost:27017/admin?authSource=admin"use local_life db.serverStatus()// 查看服务器状态(应成功,因为 clusterMonitor 角色有权限)db.products_idx.findOne()// → 错误: not authorized(clusterMonitor 没有数据读写权限)

步骤三:密码安全——修改密码与密钥轮换

目标:学习修改密码、吊销账号、启用密码检查。

// 使用 root 账号连接use admin// 修改密码db.changeUserPassword("app_life","App_Life_N3w_P@ss_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_2026@localhost:27017/local_life?authSource=local_life"use 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"/CN=MongoDB-Test-CA"# 2. 生成服务端私钥和证书签名请求openssl genrsa-outmongod.key2048openssl req-new-keymongod.key-outmongod.csr\-subj"/CN=localhost"# 3. 用 CA 签发服务端证书openssl x509-req-inmongod.csr-CAca.crt-CAkeyca.key\-CAcreateserial-outmongod.crt-days365-sha256# 4. 合并证书和私钥为 PEM 文件catmongod.key mongod.crt>mongod.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. 检查是否开启认证constauthStatus=db.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 个)constrootCount=db.system.users.countDocuments({"roles.role":"root"})print("Root 用户数:",rootCount,rootCount>2?"⚠ 过多":"PASS")// 4. 检查是否有空密码用户constnoPwd=db.system.users.find({"credentials.SCRAM-SHA-256":{$exists:false}}).count()print("弱认证账号:",noPwd,noPwd>0?"⚠ 存在":"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_lifeconsttestAuth=db.runCommand({usersInfo:{user:"app_life",db:"admin"},showPrivileges:false})print("app_life 角色:",JSON.stringify(testAuth.users[0].roles))// 应只含 {role:"readWrite", db:"local_life"}// 2. 确认监控账号不能写数据consttestMonitor=db.runCommand({usersInfo:{user:"monitor",db:"admin"}})constmonitorRoles=testMonitor.users[0].roles.map(r=>r.role)print("monitor 是否含有 data 角色:",monitorRoles.some(r=>r.includes("readWrite")||r.includes("root"))?"FAIL":"PASS")// 3. 确认测试账号已删除consttestTester=db.system.users.findOne({user:"tester_life"})print("tester_life 是否已清理:",testTester===null?"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.7+)MySQL 近年改进更明显
审计日志企业版功能企业版 + 插件社区版两者均无内置审计
字段级加密客户端字段级加密(CSFLE)需应用层实现MongoDB 有原生方案

4.2 适用场景

MongoDB 安全配置适用

  1. 生产环境所有 MongoDB 实例——必须开启认证和最小权限。
  2. 需要多团队协作的项目——不同账号不同权限,互不干扰。
  3. 数据合规要求(GDPR/PIPL)——传输加密 + 字段级加密 + 审计。
  4. 监控和备份系统的账号隔离——专用账号避免权限泄漏。
  5. CI/CD 管道中的临时数据库——动态创建账号并在管道结束时销毁。

不适用场景

  1. 纯本机单机学习环境——开认证增加心智负担,但关闭网络端口。
  2. 只读的数据副本——如果不需要区分用户,可以用--auth但配简单密码。

4.3 注意事项

注意事项说明
认证库(authSource)root 角色在 admin 库,业务用户在对应业务库,连接时必须正确指定
密码明文配置文件中勿写明文密码,用环境变量或 Secret Manager(Vault/AWS Secrets Manager)
匿名角色readAnyDatabase小心授予——它允许读所有数据库,含 system 集合
未加密的公网 MongoDB从 2017 年起已有数万起勒索攻击案例,永久不要公网裸奔
TLS 证书过期自签证书有效期设为定期提醒项,过期会导致所有连接中断

4.4 常见踩坑经验

故障案例一:authSource 配错导致连接失败

新来的运维在 Connection String 中写了mongodb://app_life:pwd@host:27017/local_life,没加?authSource=admin,结果连接报错 “Authentication failed”。根因:app_life是在 admin 库中创建的(root/admin 账号都是),而连接串默认以目标数据库local_life作为认证库。解决:连接串末尾加?authSource=admin,或在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.yml,3 天后才发现。此时黑客已通过该账号全量导出了用户数据。根因:密码泄露后的响应太慢,且账号有全量readWrite权限。解决:事后应对——立即修改密码 + 检查审计日志;事前预防——使用 GitHub Secret Scanning +.gitignore明文配置文件 + 密码存入环境变量。

4.5 思考题

  1. 如果一个用户被授予了readWrite在 database_A,又被授予了read在 database_B,他能跨库做$lookup从 database_A 关联 database_B 的数据吗?
  2. MongoDB 的客户端字段级加密(CSFLE)是如何实现"数据库管理员也看不到明文"的?加密和解密在哪里发生?

(答案将在第 14 章末尾揭晓)


上一章思考题答案

  1. Spring Data MongoDB 的分页Page<Product>底层使用的是skip + limit,而非游标分页。它在构造Query时自动加入.skip(page * size).limit(size),不涉及游标编码/解码。这也是为什么用 Spring Data 默认分页翻到深页性能会急剧下降——skip 机制的本质问题无法绕过。若需高性能深分页,必须手动实现游标分页(用 MongoTemplate 构造$gt/$lt条件)。

  2. Spring Boot 的读写分离配置:在MongoClientSettings中设置readPreference(ReadPreference.secondary())。一致性陷阱——写后立刻读可能从 Secondary 读不到刚写的数据(未复制完成);Secondary 延迟较大时可能读到过期数据。对策:要求强一致性的读操作在MongoTemplate查询时临时覆写readPreferenceprimary(但会导致读写分离失效,该查询的流量回到主库)。

延伸阅读与资源

python入门:Rquests从菜鸟脚本到企业级SDK的网络实战圣经
Milvus向量数据库实战修炼:从 0 到 1精通向量检索与生产落地
后端工程师的 AI 转型第一课:Ollama 与私有化大模型实战
10倍开发者的 Dify 魔法书:从零构建全栈 AI 应用
后端工程师转型AI第一课-Ollama 与私有化大模型实战

大型语言模型(LLM) vLLM 高性能推理落地实战

Agent开发之LlamaIndex 实战修炼与源码进阶

大语言模型Transformers 实战修炼与源码剖析

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询