安全与加密 — CodeRoadMap安全与加密
这一课你将能做到
- 开启 MongoDB 认证,创建最小权限的业务账号,并理解内置角色(read / readWrite / dbAdmin)
- 说清楚 RBAC、TLS 传输加密、存储加密(Encryption at Rest)、可查询加密(Queryable Encryption)各自防什么
- 配置审计日志思路,知道敏感字段(手机号、身份证)在 MongoDB 里的保护手段
为什么要学
MongoDB 勒索事件臭名昭著:早年大量实例裸奔在公网(无认证 + 绑定 0.0.0.0),被脚本批量扫描、拖库、删库、留勒索信。原因不是 MongoDB 不安全,而是默认配置宽松 + 使用者没意识。
真实业务里安全是分层的:网络层(谁能连)、认证层(你是谁)、授权层(你能干什么)、传输层(窃听)、存储层(拖库后的底牌)。这一课按层讲清楚,每层给出可操作的配置。
先建立图像
五道防线:
① 网络 bind_ip / 防火墙 / VPC → 谁能摸到 27017
② 认证 SCRAM / x.509 / LDAP → 连上后证明你是谁
③ 授权 RBAC 角色 → 你对哪个库有什么权限
④ 传输 TLS → 链路上不被窃听/篡改
⑤ 存储 WiredTiger 加密 / 字段级加密 → 磁盘被拖走也读不出明文
内置角色速查:
| 角色 | 能力 | 典型用途 |
|---|
| read | 读指定库 | 报表、只读账号 |
| readWrite | 读写指定库 | 业务应用账号(最常用) |
| dbAdmin | 管索引/统计,不能读写数据 | DBA 运维 |
| userAdmin | 管用户和角色 | 账号管理 |
| root | 所有权限 | 紧急使用,平时禁用 |
跟着做
步骤 1:先建管理员,再开认证(顺序不能反)
先在未开认证的实例上建管理员:
mongosh --port 27017
use admin
db.createUser({
user: "admin",
pwd: "Str0ng!Passw0rd",
roles: [ { role: "userAdminAnyDatabase", db: "admin" } ]
})
重启实例并开启认证(Docker 示例):
docker stop m1 && docker rm m1
docker run -d --name m1 --net mongo-net -p 27017:27017 mongo:7 \
--replSet rs0 --bind_ip_all --keyFile /etc/mongo-keyfile --auth
(复制集节点间认证用 keyFile,单机直接 --auth。)
认证连接:
mongosh "mongodb://admin:Str0ng!Passw0rd@localhost:27017/admin"
步骤 2:给应用建最小权限账号
本站的应用只需要读写 codeforge 库:
use admin
db.createUser({
user: "codeforge_app",
pwd: "App!Pass2026",
roles: [ { role: "readWrite", db: "codeforge" } ]
})
再建一个数据分析用的只读账号:
db.createUser({
user: "codeforge_bi",
pwd: "Bi!Pass2026",
roles: [ { role: "read", db: "codeforge" } ]
})
mongosh "mongodb://codeforge_bi:Bi!Pass2026@localhost:27017/codeforge"
db.submissions.find().limit(1) // ✅ 能读
db.submissions.insertOne({ userId: 1 }) // ❌ 报 not authorized
db.adminCommand({ listDatabases: 1 }) // ❌ 无权跨库
步骤 3:自定义角色 —— 比内置更细
只允许读 submissions、且只能更新 progress 集合:
use admin
db.createRole({
role: "progressWriter",
privileges: [
{ resource: { db: "codeforge", collection: "submissions" }, actions: ["find"] },
{ resource: { db: "codeforge", collection: "progress" }, actions: ["find", "update"] }
],
roles: []
})
步骤 4:TLS 传输加密(生产必开)
openssl req -newkey rsa:2048 -nodes -keyout mongo.key -x509 -days 365 -out mongo.crt -subj "/CN=localhost"
cat mongo.key mongo.crt > mongo.pem
mongod --tlsMode requireTLS --tlsCertificateKeyFile /etc/mongo/mongo.pem
mongosh "mongodb://localhost:27017/?tls=true&tlsAllowInvalidCertificates=true"
(tlsAllowInvalidCertificates 仅用于自签名学习环境。)
步骤 5:存储加密与字段级加密
- Encryption at Rest:WiredTiger 存储引擎级加密,企业版/Atlas 开箱即用,社区版可借助磁盘加密(LUKS、云盘加密)实现等效保护。防的是「磁盘/备份文件被拷走」。
- Queryable Encryption(7.x):字段级加密,密文上还能做等值查询。敏感字段(手机号、身份证)应用侧无感知:
// 概念示意:带加密 schema 的集合,明文永远不会出现在服务端
// phone 字段加密但仍可等值查询
db.users.insertOne({ userId: 1024, phone: "13800001234" })
// 磁盘上 phone 是密文;db.users.find({phone: "13800001234"}) 仍可命中
Queryable Encryption 需要驱动配合(CSFLE 客户端加密库),密钥放 KMS(如阿里云 KMS、AWS KMS),MongoDB 服务端只见密文。
步骤 6:审计日志(企业版)
mongod --auditDestination file --auditFormat JSON --auditPath /var/log/mongo/audit.json \
--auditFilter '{ atype: { $in: ["authCheck", "createCollection", "dropCollection"] } }'
社区版退而求其次:开启 profiler 记录慢操作 + 应用层操作日志 + 云厂商的数据库审计服务。
讲清楚要点
- 先建用户再开认证:顺序反了会把自己锁在门外(localhost exception 只允许在无任何用户时建第一个管理员)。
- 最小权限原则:应用账号只给 readWrite 到具体库,绝不给 root;BI 给 read;删表权限单独收紧。
- 认证库(authSource):用户在哪个库创建,连接串里 authSource 就写哪个库。建在 admin 就
authSource=admin,连错库报认证失败是新手第一大坑。
- bind_ip 默认 localhost:要对外服务必须显式配置,且必须同时开认证。公网裸奔 = 等着被勒索。
- TLS 防链路窃听:副本集节点间、客户端到服务端都应 TLS;节点间另用 keyFile 或 x.509 做内部认证。
- 加密分三层想清楚防谁:磁盘加密防物理拖库;字段级加密防「能登录数据库的管理员」和「服务端被攻破」;Queryable Encryption 让加密字段还能查(等值),范围查询支持在 7.x 逐步增强。
- 备份也是攻击面:mongodump 导出的是明文 BSON,备份文件必须加密存储、限制访问。
你可能会懵的地方
- 有了账号密码就安全了吗?
只过了第 ② 关。密码在网络明文传输(没 TLS)等于没设;权限给太宽(root)等于没授权概念。安全是分层叠加的。
- SCRAM 是什么?
MongoDB 默认认证机制:密码加盐哈希存储、挑战-应答握手,不在网络上传明文密码。7.x 默认 SCRAM-SHA-256。
- 字段级加密后索引还能用吗?
Queryable Encryption 支持加密字段上的等值查询(有专门的安全索引结构),但有性能开销,且不是所有运算符都支持。只加密真正敏感的字段。
- 管理员能看到加密字段的明文吗?
用了客户端字段级加密(CSFLE/QE):不能。密钥在 KMS,服务端和 DBA 只见密文。这是它相对磁盘加密的核心增量价值。
- IP 白名单和账号密码哪个更重要?
都重要,纵深防御。白名单把攻击面缩到内网/指定来源,账号密码兜底白名单配置失误。
错法对照
| 错法 | 问题 | 正解 |
|---|
--bind_ip 0.0.0.0 且不开认证 | 公网勒索标准剧本 | 认证 + 内网绑定 + 防火墙 |
| 应用直接用 root 连接 | 一个注入漏洞带走整个库 | 业务账号 readWrite 到具体库 |
| 密码写死在代码里提交 git | 源码泄露 = 库泄露 | 环境变量 / 密钥管理服务 |
连接串漏 authSource | 认证失败查半天 | 用户建在哪个库就写哪个库 |
| 备份文件随手丢在服务器 | 拖库不必碰线上实例 | 备份加密 + 独立访问控制 |
卡点急救
- 开认证后连不上:检查连接串
authSource;确认用户建在 admin 还是业务库;密码含 @ : 等特殊字符要 URL 编码。
- 复制集开认证后节点间不同步:节点间认证缺失——所有节点挂同一个 keyFile(内容一致、权限 400)或以 x.509 证书互认。
- 应用报 not authorized on xxx:账号对该库没角色。
db.getUser("codeforge_app", {showPrivileges: true}) 在 admin 库查实际权限,缺什么补什么(db.grantRolesToUser)。
面试常问
- MongoDB 怎么做权限控制?
RBAC:用户被授予角色,角色绑定「库/集合 + 动作」。内置角色覆盖常见场景(read/readWrite/dbAdmin/userAdmin),自定义角色可精确到集合和动作级。认证库与业务库分离(用户建在 admin)。
- 数据加密有哪些层次?
传输层 TLS;存储层 WiredTiger 加密/磁盘加密;字段级 CSFLE/Queryable Encryption——密钥在 KMS,服务端只见密文,7.x 支持密文等值查询。三者防的威胁不同,按需叠加。
- 如何防止 MongoDB 被拖库勒索?
不开公网(bind_ip + 防火墙/VPC)、强制认证、最小权限、TLS、定期加密备份、监控异常登录。被勒索的 100% 是公网裸奔实例。
自测三问
- 应用账号该给什么角色?为什么不能给 root?
- 连接串里 authSource 写错会发生什么?它该写哪个库?
- Queryable Encryption 和磁盘加密分别防住什么人?
小结一句话
MongoDB 安全是五层纵深:网络收口、认证验身、RBAC 限权、TLS 护路、字段级加密保底——任何一层缺失都是给攻击者留门。
延伸