MongoDB只读用户必须使用内置角色read或readAnyDatabase,优先选read并绑定具体数据库;创建时需在对应数据库执行,正确声明roles数组;连接必须指定authSource(authenticationDatabase),否则认证失败;高版本需禁用findAndModify等潜在越权操作。

创建只读用户前必须确认角色权限模型
MongoDB 的只读能力不是靠自定义权限拼凑出来的,而是依赖内置角色。最常用的是 read(库级只读)和 readAnyDatabase(集群级只读),但二者行为差异很大:前者只能查指定数据库,后者能查所有数据库(包括系统库),且需要在 admin 数据库中创建。如果误用 readAnyDatabase 又没限制 IP 或网络范围,等于开了个只读后门。
- 普通业务场景下,优先用
read角色,绑定到具体业务库,例如myapp_db - 监控或运维脚本需要跨库查
system.profile或local.oplog.rs时,才考虑readAnyDatabase - 不要给只读用户加
clusterMonitor以外的集群角色——它本身不含数据读取权,仅开放serverStatus等元信息
使用 db.createUser() 创建只读用户的正确写法
命令必须在目标数据库(read)或 admin(readAnyDatabase)中执行,且不能省略 roles 数组结构。常见错误是把角色名写成字符串而非对象,或漏掉 db 字段。
use myapp_db
db.createUser({
user: "ro_user",
pwd: "strong_password_123",
roles: [{ role: "read", db: "myapp_db" }]
})
若需跨库只读:
use admin
db.createUser({
user: "ro_monitor",
pwd: "monitor_pass_456",
roles: ["readAnyDatabase"]
})
-
pwd在 MongoDB 6.0+ 默认使用 SCRAM-SHA-256 加密,旧客户端可能不兼容,必要时加mechanisms: ["SCRAM-SHA-1"] - 密码不能含空格或特殊字符如
@、/,否则连接字符串解析会失败 - 创建后立即测试:
mongo -u ro_user -p strong_password_123 --authenticationDatabase myapp_db myapp_db --eval "db.orders.find().limit(1)"
连接时 authenticationDatabase 错配会导致“认证成功但查不到数据”
这是最常被忽略的点:MongoDB 认证凭据存储在某个数据库的 system.users 集合里,而用户实际要访问的是另一个库。如果连接时没指定 --authenticationDatabase,客户端默认去目标库找用户凭证,自然失败。
- 用户在
myapp_db中创建 → 连接必须带--authenticationDatabase myapp_db - 用户在
admin中创建 → 必须带--authenticationDatabase admin,哪怕你只是想查test库 - URI 格式示例:
mongodb://ro_user:pass@localhost:27017/myapp_db?authSource=myapp_db,其中authSource就是authenticationDatabase
只读用户仍可能意外修改数据的边界情况
read 角色理论上不能写,但某些操作会绕过限制:比如对 capped collection 执行 db.collection.insert() 会静默失败,但 db.collection.drop() 直接报错;更隐蔽的是 findAndModify 类命令——即使只设 query 不设 update,只要请求体里带了 remove: true 或 upsert: true,就可能触发权限检查漏洞(极少数旧版本存在)。所以生产环境务必禁用 findAndModify 相关驱动选项。
- 确认 MongoDB 版本 ≥ 4.4,旧版
read角色对system.*集合权限控制不严 - 应用层禁止构造含
remove、upsert、new字段的原始命令,一律走find()+ 显式判断逻辑 - 用
db.runCommand({connectionStatus: 1})查当前用户实际生效的角色,比看创建语句更可靠

















