[第三篇 运维与安全管理] ==> 第8章 数据库安全管理与审计
MongoDB 数据库安全管理与审计
- 8.1 权限管理简介
- 8.2 用户管理
- 8.2.1 创建用户与登录
- 8.2.2 查询用户
- 8.2.3 修改用户
- 8.2.4 删除用户
- 8.2.5 授予用户权限
- 8.2.6 撤销用户权限
- 8.3 角色管理
- 8.3.1 内建角色
- 8.3.2 创建自定义角色
- 8.3.3 查询自定义角色
- 8.3.4 修改自定义角色
- 8.3.5 删除自定义角色
- 8.3.6 授予角色权限
- 8.3.7 撤销角色权限
- 8.4 身份验证
- 8.4.1 SCRAM
- 8.4.2 x.509
- 1.外部认证
- 2. 内部认证
- 3. TLS/SSL 安全传输
- 8.5 数据加密
- 8.5.1 动态数据加密(传输加密)
- 8.5.2 静态数据加密
- 8.6 审计
- 8.6.1 审计的启用与配置
- 8.6.2 审计事件与过滤
- 8.7 检测安全漏洞
- 1. 审计MongoDB 服务器工具 - mongoaudit
- 2.检测MongoDB 是否暴露在网络上
安全性对于数据库管理来说是非常重要的。MongoDB 在这方面提供了多种功能,如: 身份验证、访问控制、
数据加密等。这些功能都可以进一步提升 MongoDB 的安全性。
本章将介绍如何对MongoDB 进行安全管理,让我们在操作数据库时更有保障。
通过本章,读者将学习以下内容:
- MongoDB 用户管理;
- MongoDB 角色管理;
- MongoDB身份验证;
- MongoDB 数据加密;
- MongoDB 审计。
8.1 权限管理简介
- 基于角色的访问控制规则
MongoDB 的权限管理采用的是基于角色的访问控制规则,这表示同一个用户(User)可能会被予一个或多个角色(Role)。
但是,除拥有被授予的角色权限外,用户无权做其他操作。
- 启用访问控制
MongoDB 默认不启用访问控制,所以不需要输入用户的账号和密码就可以访问数据库。如果想对
户进行访问控制,则必须启用访问控制。
单机和集群分别有不同的启用方法。
(1)如果 MongoDB 是单机,则可通过以下两种方法开启访问控制(选其一即可):
-
在用命令启动 MongoDB 时,通过参数"–auth"启用。
-
在启动 MongoDB 前,在配置文件中将 security.authorization 设定为 enable。
(2)如果 MongoDB 是集群,则在启动 MongoDB 前,在配置文件中设置 securily.keyFile 参数
来用访问控制,具体配置方法在 4.2.4 节有详细介绍。
- 数据库的第1个用户
若在启用访问控制后 MongoDB 里尚无用户,则 MongoDB 允许在无认证的情况下登录,然后创建
一个用户。
建议在 admin 数据库中创建第 1 个用户,并给其赋予管理者权限(如 root、userAdmin 等)。因为
创健完该管理用户后才能通过此用户去创建其他用户,不会发生无法创建其他用户或无法授权的问题。
此外,也可以在启用访问控制前就创建好主要的数据库用户,
- 角色与用户的关系
数据库中角色与用户的关系如图 8-1 所示。
(1)角色(Role):用于绑定具体操作权限与数据库,并授权给用户,使用户具有访问和操作一至
多个数据库的权限。数据库的角色又分为"内建角色"和"自定义角色"。
- 内建角色:MongoDB 本身自带的角色,每个内建角色都有预设好的权限。
- 自定义角色:允许管理者自行定义操作权限的角色(例如: find、insert 等权限)。
(2)用户(User):角色绑定的对象,表示数据库用户具体登录时的账号。
8.2 用户管理
8.2.1 创建用户与登录
可以通过赋予用户具体所需要的权限,来确保数据库访问的安全性。
创建用户的标准语法如下:
db.createUser(
{
user: "<name>",
pwd: "<cleartext password>",
customData: { <any information> },
roles: [
{ role: "<role>", db: "<database>" } | "<role>",
...
],
authenticationRestrictions: [
{
clientSource: ["<IP>" | "<CIDR range>", ...],
serverAddress: ["<IP>" | "<CIDR range>", ...]
},
...
],
mechanisms:["<SCRAM-SHA-1 | SCRAM-SHA-256>",...]
}
)
参数说明如下。
-
user: 用户的账号。
-
pwd: 用户的密码。
-
customData: 用户的描述。此参数可选择不设置。
-
roles: 将授予的角色。{role:“”,db:“”}中的 role 表示授予的角色, db表示
此角色用于指定的数据库中。若没有指定 db,则默认为当前数据库。 -
authenticationRestrictions: 此为可选参数。在 MongoDB 3.6 之后的版本中提供,用来管控
用户进行登录的IP 地址, 可绑定用户客户端IP 地址及用户访问的 MongoDB 服务器端IP 地址。- clientSource: 限制用户客户端IP 地址。
- serverAddress: 限制用户访问的 MongoDB 服务器端IP 地址。
-
mechanisms:可选参数。在 MongoDB 4.0之后的版本中提供,可以指定新用户采用的SCRAM
机制(SCRAM 的详细说明将会在 8.4.1 节"SCRAM"中介绍)。
启用权限管控以后,在登录 MongoDB 时,需要加上权限相关参数,格式如下:
mongo -u <username>-p <password>--authenticationDatabase <database>
其中,权限参数除有账号和密码外,还有认证的数据库名( 即此账号是创建于哪个数据库下)。
在登录时,若想以密码提示的方式输入,则只需使用参数"-p", 参数后不需输入密码。
此方法可避免登录时密码以明码显示。
1.创建用户
(1)创建具有"readWrite"角色的用户。
在创建用户时,必须先切换至将用作认证的数据库,然后再进行创建。
[范例]在"mall"数据库中创建了一个具有"readWrite"角色的新用户"test"。
切换至"mall"数据库,具体指令如下:
> use mall
创建用户的具体指令如下:
db.createUser(
{
user: "test",
pwd: "123456",
customData: {"des": "这是给测试人员使用的账号"},
roles: ["readWrite"]
}
)
(2)创建管理员用户
接下来将创建具有管理员角色的用户。此用户必须在管理数据库"admin"中创建。
[范例]创建管理用户"devAdmin",并给其授予"userAdminAnyDatabase"角色的权限。
切换至"admin"数据库后,创建管理员用户。具体指令如下:
db.createUser(
{
user: "devAdmin",
pwd: "123456",
customData: {"des":"这是给开发管理员使用的账号"},
roles: [ { role: "userAdminAnyDatabase", db: "admin" } ]
}
)
(3)创建具有身份验证限制的用户。
创建具有IP 地址管控的用户是 MongoDB 3.6 版本之后的新功能。在创建用户时,可以绑定用户IP
地址,以及限制用户访问的服务器 IP 地址。通过设置"authenticationRestrictions"参数中的
"clientSource"或"serverAddress"以达到身份验证的管控。使用此参数会出现3种可能的组合,如
图8-2所示。
接下来创建一个具有身份验证限制的用户:在"mall"数据库中创建一个名为"operate01"的用户,
并限制客户端P 地址为"172.16.46.112",仅能够连接到的服务器IP 地址为"192.168.250.101", “192168.250.102”。
切换至"Product"数据库后,创建具有IP 地址验证管控的用户。具体指令如下:
db.createUser(
{
user: "operate01",
pwd: "123456",
customData: {"des": "这是给运营人员使用的账号"},
roles: ["readWrite"],
authenticationRestrictions: [{
clientSource: ["172.16.46.112"],
serverAddress: ["192.168.250.101","192.168.250.102"]
}]
}
)
2.登录启用身份验证的 MongoDB
在身份验证功能开启的情况下,登录 MongoDB 需要输入账号、密码和认证的数据库,可以选择完整的登录语句登录,也可以用密码提示的方式登录。
出于安全性的考虑,建议使用密码提示的方式输入。
使用前面所创建的一般用户"test"登录。
登录命令如下:
mongo -u test -p 123456 --authenticationDatabase mall
使用密码提示的方式登录,具体命令如下:
mongo -u test -p --authenticationDatabase mall
8.2.2 查询用户
查询用户信息有以下两种方式。
- db.getUser(): 查询指定用户。
- db.getUsers(): 查询所有用户。
- 查询指定用户
(1) 语法格式:
db.getUser(<username>, <args>)
(2)参数说明(前面已介绍过的参数,此处就不再述)。
args 为可选参数,可分成三种配置。
-
showCredentials:是否显示加密后的密码相关信息,默认为 false。
-
showPrivileges:是否显示用户的全部权限,默认为 false。在查询所有的用户时,此参数不设置成true。
-
showAuthenticationRestrictions:是否显示用户的IP 地址管控限制,默认为 false。在查询所有的用户时,此参数不能设置成 true。
(3) 范例:
查询"mall"数据库中用户"operate01"的信息,并显示此用户的所有信息。
切换至"mall"数据库后,查询指定用户"operate01",具体指令如下:
db.getUser(
"operate01",
{
showCredentials: true,
showPrivileges: true ,
showAuthenticationRestrictions : true
}
)
显示结果:
{
_id: 'mall.operate01',
userId: UUID('500b251c-ccff-4be7-bb8f-55ce2d7d3af7'),
user: 'operate01',
db: 'mall',
mechanisms: [ 'SCRAM-SHA-1', 'SCRAM-SHA-256' ],
# 显示加密后的密码相关信息
credentials: {
'SCRAM-SHA-1': {
iterationCount: 10000,
salt: '70k9vsWTbgHSSKVg2cSw0Q==',
storedKey: 'YzW4xI8MarbyWp8QWLVQZB46R1A=',
serverKey: 'i+DnNP1Iw1IKx4pwHsSrYUdI2hw='
},
'SCRAM-SHA-256': {
iterationCount: 15000,
salt: '4yUtd3drFEUnmB1UYdH7rigYoj0fpCgye39KRQ==',
storedKey: '9QUWQbFHlhz47nTW2Pmxhq9cZjIKGDyHY1wDZU1UVQA=',
serverKey: 'NK5a6VqwagpjGmmThgTWlm7IA031yf3zZvpUKFXSDfU='
}
},
# 显示用户的IP 地址管控限制
authenticationRestrictions: [ { clientSource: [Array], serverAddress: [Array] } ],
customData: { des: '这是给运营人员使用的账号' },
roles: [ { role: 'readWrite', db: 'mall' } ],
inheritedRoles: [ { role: 'readWrite', db: 'mall' } ],
# 显示用户的全部权限
inheritedPrivileges: [
{ resource: [Object], actions: [Array] },
{ resource: [Object], actions: [Array] }
],
inheritedAuthenticationRestrictions: []
}
- 查询所有用户
(1) 语法格式:
db.getusers(
{
showCredentials: <Boolean>,
filter: <document>
}
)
(2)参数说明(前面已介绍过的参数,此处就不再赘述 )。
filter 是在 MongoDB4.0 以后的版本中提供的,可设置筛选条件,以查询匹配的用户信息。
(3)范例。
查询"mall"数据中使用"SCRAM-SHA-256"安全认证机制创建的所有用户信息。
切换至"mall"数据库后,查询所有用户并加上筛选条件,具体指令如下:
db.getUsers( { filter: { mechanisms: "SCRAM-SHA-256" } } )
显示结果如下:
{
users: [
{
_id: 'mall.operate01',
userId: UUID('500b251c-ccff-4be7-bb8f-55ce2d7d3af7'),
user: 'operate01',
db: 'mall',
customData: [Object],
roles: [Array],
mechanisms: [Array]
},
{
_id: 'mall.test',
userId: UUID('28517a36-a117-4f49-8ca2-f68efb93ac56'),
user: 'test',
db: 'mall',
customData: [Object],
roles: [Array],
mechanisms: [Array]
}
],
ok: 1
}
8.2.3 修改用户
如需要变更用户属性,则可以使用 db.updateUser()方法来实现。
[范例]修改"mall"数据库中的用户"test",将其描述改为"the test user",并且给其加另一个数据库"Store"的"read"权限。
操作如下。
切换至"mall"数据库后,修改用户"test",具体指令如下:
db.updateUser(
"test",
{
customData: {"desc":"the test user"},
// 如果要保留原角色,则在修改时也要将原角色加上
roles: [ "readWrite", { role: "read", db: "Store" } ]
}
)
8.2.4 删除用户
将用户从当前数据库中删除,有以下两种方式。
- db.dropUser(): 删除指定用户。
- db.dropAlUsers(): 删除所有用户。
在执行删除用户操作前,必须确保 MongoDB 中至少存在一个拥有管理者权限的用户,否则无法执行更高权限的操作。
若发生所有管理者用户都被误删的情况,则可以利用以下步骤补救:
(1)在配置文件中关闭访问管控参数后重启。
(2)登录 MongoDB,创建具有账号管理权限的用户。
(3)在配置文件中开启访问控制参数后重启。
- 删除指定用户
(1)语法格式:
db.dropUser(<username>, <writeConcern>)
(2)参数说明(前面已介绍过的参数,此处不再述)。
writeConcern 为可选参数,用来设定是否使用写入策略(写入策略的详细说明请查看 3.2.3 节"数据读写策略")。
(3) 范例。
删除"mall"数据库中的用户"test"。
切换至"mall"数据库后,删除用户"test",具体指令如下:
db.dropUser("test")
如果返回true则代表删除test用户成功。
2.删除所有的用户
(1)语法格式:
db.dropAlUsers(<writeConcern>)
(2)范例:
删除“Product”数据库中的所有用户。
切换至"mall"数据库后,删除"mall"数据库中的所有用户。具体指令如下:
db.dropAlUsers()
执行的结果如下:
NumberLong(1)
表示"mall"总共有一个用户,且已删除。
8.2.5 授予用户权限
如果用户账号存在,现在要对此用户授予额外的权限,则需要使用 db.grantRolesTouser0来进行
角色授权。
(1)语法格式:
mth.grantRolesToUser( "<username>", [ <roles> ], { <writeConcern> } )
(2) 范例:
在"mall"数据库中,给"test"授予"Store"数据库的"read"权限。
切换至"mall"数据库后,授予用户权限,具体指令如下:
db.grantRolesToUser(
"test",
[ {role: "read", db: "Store"}]
)
8.2.6 撤销用户权限
在已授权的用户中撤销指定的角色权限,可以使用 db.revokeRolesFromUser()。
(1)语法格式:
db.revokeRolesFromUser( "<username>", [ <roles> ] , { <writeConcern> })
(2)范例
在"mall"数据库中,撤销"test"在"Store"数据库中的"read"权限。
切换至"mall"数据库后,撤销用户权限,具体指令如下:
db.revokeRolesFromUser(
"test",
[ {role: "read", db: "Store"}]
)
8.3 角色管理
8.3.1 内建角色
MongoDB主要是通过角色绑定的方式授予用户权限,而 MongoDB 已经提供了一些内建角色。
可以让用户在不同数据库中进行不同级到的访问。除此之外,也可以创建自定义角色,这部
分内容将会在8.3.2节中介绍。
- 内建角色的特点
内建角色具有以下特点:
-
授予用户角色权限时,默认授权于当前的数据库。
-
角色授权可以授予集合级别的粒度。
-
角色授权分成系统集合以及非系统集合的访问权限。
-
每个数据库中的角色都可以分成一般角色和管理角色。
-
管理数据库(admin)可以使用所有的内建角色。
- 内建角色的种类
MongoDB 中有7个不同级别的内建角色,如图 8-3 所示。
-
图8-3内建角色
(1)数据库用户角色。 -
read:用于读取所有非系统集合,以及以下3 个系统集合的数据。
-
system.indexes
-
system.js
-
system.namesp
-
-
readWrite:拥有read角色的所有权限,并且可以修改所有非系统集合 和 system.js集合上的数据。
(2)数据库管理角色。
-
dbAdmin:提供管理相关的功能,如:查询数据库统计信息、索引管理等。
-
userAdmin:提供管理数据库角色及用户的权限。具有这个角色的用户可以为当前数提库的
任何用户(包括自己)分配任何角色权限。 -
dbOwner:提供数据库所有者的权眼,它可以对数据库执行任何管理操作。这个角色结合了
readWrite、dbAdmin 和 userAdmin 三种角色授予的权限。
(3)集群管理角色。
此类角色提供管理整个 MongoDB 的权限,角色只能在 admin 数据库中进行授权,它包含了以下
四种不同功能的角色。
-
clusterManager:提供对集群进行管理和监控的权限。
-
clusterMonitor:提供对监控工具(如 MongoDB Cloud Manager 和 Ops Manager 监控代理)只读的访问权限。
-
hostManager:提供监控和管理服务器的权限。
-
clusterAdmin:提供最高的集群管理访问权限,这个角色拥有 clusterManager、clusterMonitor
和hostManager 角色授予的权限,除此之外也提供了 dropDatabase 的权限。
(4)备份和恢复角色。
此类角色只能在admin 数据库中授权备份以及恢复,具有以下两种角色。
-
backup:提供备份数据的权限,且这个角色可以进行以下操作一使用 MongoDB Cloud Manager 备份代理,使用 mongodump 备份整个 mongod 实例。
-
restore:提供还原数据所需的权限,使用户可以使用 mongorestore 恢复数据。
(5)全数据库角色。
全数据库角色用于管理所有数据库。此类角色有以下两个特点需要注意:
-
只能授权于admin 数据库中的用户。
-
不适用于local和 config 数据库。
全数据库角色分成以下四种不同功能的角色。
-
readAnyDatabase:提供所有数据库只读权限。
-
redWriteAnyDatabase:提供所有数据库的 readWrite 权限。
-
userAdminAnyDatabase:提供所有数据库的 userAdmin 权限。由于角色可以授予自己所有权限,因此这个角色实际上是一个 MongoDB 系统的超级用户。
-
dAdminAnyDatabase:提供所有数据库 dbAdmin 权限。
(6)超级用户角色。
角色仅能在 admin 数据库中配置权限,此权限可以对所有数起库进行任何提作, 也可以定义任何用户的权限。
root 角色就是 MongoDB 的超级用户它拥有 readWriteAnyDatabase、dbAdminAnyDatabase, userAdminAnyDatabase、
clusterAdmin、restore和backup等角色的权限。
外有以下三种权限若用在 dmin 数据库下可以替自己授予任何权限,间接达到超级用户角色的权限,说明如下:
-
dbOwner:当拥有 admin 数据库的dbOwner 角色时,可以管理其他数据库及授予所有用户任何权限。
-
userAdmin:当拥有 admin 数据库的 userAdmin 角色时,可以为所有数据库中的用户分配任何角色权限。
-
userAdminAnyDatabase:提供所有数据库的 userAdmin 权限。
(7)内部角色。
_ _svstem 主要用于 MongoDB 系统内部的操作。目前 MongoDB 官方不建议将此角色授权给任何用户或管理者,
防止用户随意对内部系统进行操作,从而影响整个数据库,除非用户需要进行MongoDB 认证这种特定的系统内部操作。
8.3.2 创建自定义角色
此类角色可以让管理者自行定义角色的权限(如 remove、update 等权限),以及可使用的数据库。
(1)自定义角色有以下几个特点:
-
在一般数据库上创建的角色,只适用于当前数据库。
-
在 admin 数据库上创建的角色,可适用于所有数据库。
-
创建角色时,角色名称不能重复。
(2) 语法格式:
db.createRole(
{
role: "<name>",
privileges: [
{ resource: { <resource> }, actions: [ "<action>", ... ] },
...
],
customData: { <any information> },
roles: [
{ role: "<role>", db: "<database>" } | "<role>",
...
],
authenticationRestrictions: [
{
clientSource: ["<IP>" | "<CIDR range>", ...],
serverAddress: ["<IP>" | "<CIDR range>", ...]
},
...
],
mechanisms:["<SCRAM-SHA-1 | SCRAM-SHA-256>",...]
}
)
(3)参数说明( 上面已介绍过的参数,此处就不再整述)。
-
role:新角色的名称。
-
privileges:授予角色的权限。
- resource:指定数据库或集合。若数据库设置为空,则默认为当前数据库;
若集合设置为空, 则默认为全部集合。 - actions:指定权限(如: remove、update 等权限)。
- resource:指定数据库或集合。若数据库设置为空,则默认为当前数据库;
-
roles:继承已存在角色的权限。
(4)范例。
在"admin"数据库中创建具有 find、inset、remove、 update 权限的角色"testUserRole",并从
其它数据库角色中继承权限,并设置此角色只能从客户端"172.16.46.112"连接"192.168.250.101"、
"192.168.250.102"节点的 MongoDB 实例上。
切换至"admin"数据库,创建自定义角色"testUserRole",具体指令如下:
db.createRole(
{
role: "testUserRole",
privileges: [
{
resource: {db:"", collection:""},
actions: ["find","insert","remove","update"]
}
],
roles: [
{role: "readWrite", db: "mall"},
{role: "read", db: "Store"},
{role: "read", db: "admin"},
],
authenticationRestrictions: [{
clientSource: ["172.16.46.112"],
serverAddress: ["192.168.250.101","192.168.250.102"]
}]
}
)
参数说明:
-
resource:如果没有指定教据库及集合,则 MongoDB会认为当前数据库的全部集合。
-
roles:在admin 数据库中,可以从其他数据库中的角色继承权限。
8.3.3 查询自定义角色
可使用以下两种方法查询自定义角色。
- db.getRole): 查询指定的自定义角色。
- db.getRoles(): 查询所有自定义角色。
- 查询指定的自定义角色
(1)语法格式:
db.getRole(<rolename,args>)
(2)参数说明。
args为可选参数,设定时有两种值。
-
showPrivileges:是否显示自定义角色的全部权限,默认为false。
-
showAuthenticationRestrictions:是否显示自定义角色的IP地址管控限制,默认为false。
(3)范例。
查询在"admin"数据库中的自定义角色"testUserRole",并显示此角色所有的详细信息。
切换至"admin"数据库,查询自定义角色"testUserRole",具体指令如下:
db.getRole(
"testUserRole",
{
showPrivileges: true,
showAuthenticationRestrictions: true
}
)
执行的结果如下:
{
_id: 'admin.testUserRole',
role: 'testUserRole',
db: 'admin',
// 授予的权限
privileges: [
{
resource: { db: '', collection: '' },
actions: [ 'find', 'insert', 'remove', 'update' ]
}
],
// 继承的权限
roles: [
{ role: 'readWrite', db: 'mall' },
{ role: 'read', db: 'Store' },
{ role: 'read', db: 'admin' }
],
// 连接IP管控配置
authenticationRestrictions: [
[
{
clientSource: [ '172.16.46.112' ],
serverAddress: [ '192.168.250.101', '192.168.250.102' ]
}
]
],
inheritedRoles: [
{ role: 'readWrite', db: 'mall' },
{ role: 'read', db: 'Store' },
{ role: 'read', db: 'admin' }
],
// IP管控
inheritedAuthenticationRestrictions: [
[
{
clientSource: [ '172.16.46.112' ],
serverAddress: [ '192.168.250.101', '192.168.250.102' ]
}
]
],
isBuiltin: false
}
- 查询所有的自定义角色
(1)语法格式:
db.getRoles(<args>)
(2)范例:
查询"admin"数据库中所有的自定义角色,并显示这些角色所有的详细信息。
切换至"admin"数据库,查询所有的自定义角色,具体指令如下:
db.getRoles(
{
showPrivileges: true,
showAuthenticationRestrictions: true
}
)
执行的结果如下:
{
roles: [
{
_id: 'admin.testUserRole',
role: 'testUserRole',
db: 'admin',
privileges: [
{
resource: { db: '', collection: '' },
actions: [ 'find', 'insert', 'remove', 'update' ]
}
],
roles: [
{ role: 'readWrite', db: 'mall' },
{ role: 'read', db: 'Store' },
{ role: 'read', db: 'admin' }
],
authenticationRestrictions: [
[
{
clientSource: [ '172.16.46.112' ],
serverAddress: [ '192.168.250.101', '192.168.250.102' ]
}
]
],
isBuiltin: false,
inheritedRoles: [
{ role: 'readWrite', db: 'mall' },
{ role: 'read', db: 'Store' },
{ role: 'read', db: 'admin' }
],
inheritedAuthenticationRestrictions: [
[
{
clientSource: [ '172.16.46.112' ],
serverAddress: [ '192.168.250.101', '192.168.250.102' ]
}
]
]
}
],
ok: 1
}
8.3.4 修改自定义角色
更新自定义角色使用 db.updateRole(), 此方式会使得新权限完全取代原本角色的权限。若只需要
修改其中几个角色 或 权限,可以使用 db.grantRolesToRole()、grantPrivilegesToRole()、db.revokeRolesFromRole()、db.revokePrivilegesFromRole()四种方法。
[范例]将"admin"数据库中的"testUserRole"角色更改为仅能对"mall"数据库进行"insert"操作,并
继承"Store"数据库的"readWrite"权限。
切换至"admin"数据库后,修改自定义角色"testUserRole":
db.updateRole(
"testUserRole",
{
privileges: [
{
resource: {db:"mall", collection:""},
actions: ["insert"]
}
],
roles: [
{role: "readWrite", db: "Store"}
]
}
)
8.3.5 删除自定义角色
删除自定义角色可使用以下两种方法。
- db.dropRole():删除指定的自定义角色。
- db.dropAlIRoles():删除全部自定义角色
1.删除指定的自定义角色
(1)语法格式:
db.dropRole(<rolename>, <writeConcern>)
(2) 范例:
删除"admin"数据库中的自定义角色"testUserRole"。
切换至"admin"数据库,删除自定义角色"testUserRole",具体指令如下:
db.dropRole("testUserRole")
执行的结果如下;
true
2.删除所有的自定义角色
(1) 语法格式
db.dropAlRoles(<writeConcern>)
(2) 范例:
删除"admin"数据库中所有的自定义角色。
切换至"admin"数据库,删除所有的自定义角色,具体指令如下:
db.dropAllRoles()
执行的结果如下:
{ n: 1, ok: 1 }
表示admin 总共有1个自定义角色,且已删除。
8.3.6 授予角色权限
若要在已存在的用户上添加角色或权限,有以下两种方法。
(1) db.grantRolesToRole():将指定"角色"授予给自定义角色。
(2) db.grantPrivilegesToRole():将指定"权限"授予给自定义角色。
- 将角色授予给自定义角色
(1) 语法格式:
db.grantRolesToRole("<roleName>", [<roles>], {<writeConcern>})
(2)范例:
先创建一个名为operateUserRole
的角色
db.createRole(
{
role: "operateUserRole",
privileges: [
{
resource: {db:"Order", collection:""},
actions: ["find","insert","remove","update"]
}
],
roles: [
{role: "readWrite", db: "Order"}
],
}
)
将"mall"数据库中的自定义角色"operateUserRole",授予给"admin"数据库中的自定义角色
“testUserRole”。
切换至"admin"数据库,将角色授予给自定义角色"testUserRole",具体指令如下:
db.grantRolesToRole(
"testUserRole",
[{role: "operateUserRole", db: "admin"}]
)
执行完成后,再次查看testUserRole
角色的信息,输出如下:
{
_id: 'admin.testUserRole',
role: 'testUserRole',
db: 'admin',
roles: [
{ role: 'readWrite', db: 'mall' },
{ role: 'operateUserRole', db: 'admin' },
{ role: 'read', db: 'admin' },
{ role: 'read', db: 'Store' }
],
inheritedRoles: [
{ role: 'operateUserRole', db: 'admin' },
{ role: 'read', db: 'Store' },
{ role: 'read', db: 'admin' },
{ role: 'readWrite', db: 'Order' },
{ role: 'readWrite', db: 'mall' }
],
isBuiltin: false
}
- 将权限授予给自定义角色
(1)语法格式:
db.grantPrivilegesToRole(
"<rolename>",
[
{resource: { <resource> , actions: [ "<action>", ... ]} },
...
],
{< writeConcern >}
)
(2) 范例:
在"admin"数据库的自定义角色"testUserRole"中增加"createUser"、"createRole"两个权限。
切换至"admin"数据库,将权限授予给自定义角色"testUserRole",具体指令如下:
db.grantPrivilegesToRole(
"testUserRole",
[
{
resource: { db: "", collection: "" },
actions: ["createUser", "createRole"]
}
]
)
查询testUserRole
最新的权限信息
{
_id: 'admin.testUserRole',
role: 'testUserRole',
db: 'admin',
privileges: [
{
resource: { db: '', collection: '' },
actions: [
'createRole',
'createUser',
'find',
'insert',
'remove',
'update'
]
}
]
}
8.3.7 撤销角色权限
如果需要在现有的角色权限中撤销其中的几个角色权限,则可以使用以下两种方法。
-
db.revokeRolesFromRole): 删除自定义角色中的指定角色。
-
db.revokePrivilegesFromRole(): 删除自定义角色中的指定权限。
- 删除自定义角色中的指定角色
(1) 语法格式:
db.revokeRolesFromRole("<roleName>", [<roles>], {<writeConcern>})
(2)范例:
切换至"admin"数据库,删除自定义角色"testUserRole"中的"operateUserRole"角色,
具体指令如下:
db.revokeRolesFromRole(
"testUserRole",
["operateUserRole"]
)
执行完成后,再次查看testUserRole
角色的信息,输出如下:
{
_id: 'admin.testUserRole',
role: 'testUserRole',
db: 'admin',
roles: [
{ role: 'readWrite', db: 'mall' },
{ role: 'read', db: 'admin' },
{ role: 'read', db: 'Store' }
],
inheritedRoles: [
{ role: 'read', db: 'Store' },
{ role: 'read', db: 'admin' },
{ role: 'readWrite', db: 'mall' }
],
isBuiltin: false
}
- 删除自定义角色中的指定权限
(1)语法格式:
db.revokePrivilegesFromRole(
"<rolename>",
[
{resource: { <resource> , actions: [ "<action>", ... ]} },
...
],
{< writeConcern >}
)
(2) 范例:
删除"admin"数据库的自定义角色"testUserRole"中增加"createUser"、"createRole"两个权限。
切换至"admin"数据库,将权限授予给自定义角色"testUserRole",具体指令如下:
db.revokePrivilegesFromRole(
"testUserRole",
[
{
resource: { db: "", collection: "" },
actions: ["createUser", "createRole"]
}
]
)
查询testUserRole
最新的权限信息
{
_id: 'admin.testUserRole',
role: 'testUserRole',
db: 'admin',
privileges: [
{
resource: { db: '', collection: '' },
actions: [
'find',
'insert',
'remove',
'update'
]
}
]
}
8.4 身份验证
MongoDB 社区版与企业版均支持两种身份验证机制,并通过这些机制验证用户身份。此外,
MongoDB 企业版还额外支持两种机制,但本节仅介绍两种版本都支持的验证机制。
- MongoDB 社区版与企业版都支持的验证机制
-
SCRAM (MongoDB 默认的验证机制)
-
x509证书认证
- MongoDB 企业版单独支持的验证机制
-
Kerberos 认证
-
LDAP代理认证
MongoDB 早期版本的验证机制是 MongoDB-CR。从 MongoDB 40版开始,就已经不支持它了!
8.4.1 SCRAM
SCRAM(Salted Challenge Response Authentication Mechanism)是 MongoDB 启用身份管
控后默认的验证机制。SCRAM 是基于ETF RFC 5802 标准,通过密码对用户进行身份验证。
该机制使用客户端与服务器端协商好的哈希函数对双方身份进行验证,如 MongoDB
支持的"SCRAM-SHA-1"及"SCRAM-SHA-256"机制就是分别使用了哈希函数"SHA-1"
和"SHA-256"来进行验证。
为什么要用 SCRAM 进行双方身份验证呢?
因为:
(1) 服务器端必须要验证连接的是真的客户端。
(2)客户端必须要验证连接的是真的服务器端。
通过双向身份验证,可以证明双方的真实身份,确保具有高度安全性。
圈8-4说明了SCRAM 是如何让客户端及服务器端进行双向身份验证的。
-
图8-4SCRAM验证流程
经过上述 5 个步骤可以让客户端及服务器端确认彼此的身份。在进行 SCRAM 身份验时,
MongoDB 服务器端会存取4个属性。 -
iterationCount:密码重复加密的次数。
-
salt:密码加密前的随机字符串。
-
storedKey:加密后验证客户端身份的密钥。
-
serverKey:验证服务器端身份的密钥。
下面以一个 MongoDB 实际范例说明如何在服务器端取得用户SCRAM 身份验证的4个属性"admin"数据库中用户"devAdmin"的密码相关信息。具体指令如下:
db.getUser("devAdmin", { showCredentials:1} )
输出如下:
{
_id: 'admin.devAdmin',
userId: UUID('d8a5acbe-6c47-47f1-8b7e-e82cc68d4d82'),
user: 'devAdmin',
db: 'admin',
credentials: {
'SCRAM-SHA-1': {
iterationCount: 10000,
salt: 'xqDJwUixS286XAHAoSivWA==',
storedKey: 'IDqZWD4JJXWP2TI2UvQUPOajNiY=',
serverKey: 'uMvJVnsZ0pnZH181tsdnUxkV12Y='
},
'SCRAM-SHA-256': {
iterationCount: 15000,
salt: '2c3FiJ/nNTdahc+CX9lHu2hQozaRnwfW9+vlOg==',
storedKey: 'NyZPNUf3FOFFXkQ/PqSZ2t8s9JPzc2NT1hgk5W5Afls=',
serverKey: 'qKCMMuOmGKm65lDycAsYjmSCZGSY5oyxGYBKm5/UOc0='
}
},
customData: { des: '这是给开发管理员使用的账号' },
roles: [ { role: 'userAdminAnyDatabase', db: 'admin' } ],
mechanisms: [ 'SCRAM-SHA-1', 'SCRAM-SHA-256' ]
}
8.4.2 x.509
MongoDB 支持x.509 证书身份认证,并通过TLS/SSL建立安全的连接,使客户端连接服务器端时
不要使用账号和密码进行登录,而是通过证书进行登录。
这个证书必须由数字证书认证机构(CA)颁发给客户端及服务器端。此证书不仅可以用于客户端的
认证,也可以用于 MongoDB 系统内部副本集和分片之间的身份认证。
关于x.509的使用主要包括三个方面:
(1)外部认证。
(2)内部认证。
(3)TLS/SSL 安全传输。
证书颁发机构(CA) 可以是第三方 TLS/SSL 供货商,或是自己内部组织,但内部组织颁布的证书仅能在内部局网使用。
图8-5是x.509 身份认证的流程图。
外部认证为客户端可以通过 CA授予的证书连接服务器端;内部认证为服务器端的 MongoDB 实例
之间通过CA授予的证书互相连接。其中,"PEMKeyFile"和"CAFile"是 CA在授予证书时
产生文件。CA 如何授予证书不在本节介绍范畴内。
上述“PEMKeyFile"和"CAFile"文件需在配置文件中设置。若为副本集或集群,则其中每一个
实例均须要使用相同的文件设置。
1.外部认证
使用证书认证的配置启动后,需要在 MongoDB 中建立证书用户。具体步骤如下,
(1)在MongoDB 配置文件中使用x.509外部认证。
具体配置方式如下:
security:
clusterAuthMode: x509
net:
ssl:
mode: requireSSL
# 这两个文件为配置 TLS/SSL 所需,在 CA 投予证书时产生
PEMKeyFile: <PEMKeyFile 路径>
CAFile: <CAFile 路径>
(2)使用 PEM 证书新增 MongoDB 用户。
# openssl x509 -in <PEM 文件> -inform PEM -subject -nameopt RFC2253
执行的结果如下:
subject= CN=<Name>,OU=<OrgUnit>,0=<Org>,L=<Localiyt,ST=<State>,C=<Country>
------BEGIN CERTIFICATE------
...
------END CERTIFICATE------
登录 MongoDB后,用x509 证书的 subject新增用户,具体指令如下:
db.getSiblingDB("$external")runCommand(
{
createUser: "CN=<Name>,OU=<OrgUnit>,0=<Org>,L=<Localiyt,ST=<State>,C=<Country>",
roles:[
{role: "root", db: "admin"}
]
}
)
(3)登录配置x.509 认证的 MongoDB,具体命令如下:
mongo --sslPEMkeyFile <PEMkeyFile 文件> -sslCAFile <CAFile 文件夹> --authenticaionDatabase
'$external' --authenticaionMechanism MONGODB-X509
2. 内部认证
(1)在MongoDB 配置文件中使用x.509外部认证。
具体配置方式如下:
security:
clusterAuthMode: x509
net:
ssl:
mode: requireSSL
# 这两个文件为配置 TLS/SSL 所需,在 CA 投予证书时产生
PEMKeyFile: <PEMKeyFile 路径>
CAFile: <CAFile 路径>
clusterFile: <PRMKeyFile>
clusterFile 为启用内部认证所使用的参数,同样使用"PEMKeyFile"证书即可。
必须注意,同一个集群间的 MongoDB 实例,证书上的组织(O)、组织单位 (OU)、域(DC)信息必须相同。
3. TLS/SSL 安全传输
x.509作为外部认证,可使客户端在连接 MongoDB 服务器端时具有 TLS/SSL 安全传输的功能;
x.509作为内部认证,可使 MonogDB 集群间的实例在互相连接时具有 TLS/SSL 安全传输的功能。
从MongoDB 4.0 开始,若使用的是无效证书,则可以通过参数的配置,使得连接虽无法达到认证的效果,
但还是具有TLS/SSL 安全传输的功能。
MongoDB 配置文件允许无效证书,具体配置方式如下:
security:
clusterAuthMode: x509
net:
ssl:
mode: requireSSL
# 这两个文件为配置 TLS/SSL 所需,在 CA 投予证书时产生
PEMKeyFile: <PEMKeyFile 路径>
CAFile: <CAFile 路径>
clusterFile: <PRMKeyFile>
allowInvalidCertificates: true
8.5 数据加密
MongoDB提供了动态加密和静态加密。动态加密是指,在 MongoD8 传输数据时进行加密;
而静态加密是指,对MongoDB数据库中的内部数据进行加密。
8.5.1 动态数据加密(传输加密)
动态数据加密用于 MongoD8 节点之间及节点与客户端之间数据传输。它确保数据只能由预期的目标
客户端读取,从而保障了数据的安全。它有下两种方式。
-
使用CA颁发的TLS/SSL 证书:MongoDB 会使用驱动程序验证服务器的身份。
-
使用私人组织的证书:传输的数据会被加密,但是无法验证服务器身份。
8.5.2 静态数据加密
MongoDB 还提供了静态加密功能。但静态加密功能目前只有 MongoDB 企业版才支持。
MongoDB的静态加密是存储引擎WiredTiger 提供的功能,通过它可以对数据库中的文件进行加密,
只有拥有密钥的用户才能解密并读取数据。
静态加密方式为每一个数据库实例产生一个密钥,数据库实例使用自己的密钥对数据进行加密,而这
些密钥会通过 MongoDB的主密钥加密,以保障每个实例密钥的安全性。而主密钥的创建及管理
方式持以下两种选项:
(1)通过第三方密钥管理供货商来进行创建及管理,但必须支持密钥管理互操作协议(KMIP)才可以使用。
这也是目前 MongoDB 官方所推荐的方式,且官方也有认证的合作伙伴供用户参考选择。
(2)通过"openssl"命令在服务器上创建主密钥,且此密钥需要由用户自行管理。对于 MongoDB来说,
管理密钥是一项非常重要的工作。若使用这种方式,则 MongoDB 会要求用户安全妥善地管己的密钥。
8.6 审计
MongoDB 的审计功能是指,数据库管理员可以管控每个用户对数据库所进行的操作。当审计功启时,
操作事件的呈现方式可以选择在命令提示符显示、写入系统日志、生成 JSON 或 BSON文件,
而目前这个功能只有 MongoDB 企业版才支持。
将审计功能开启后,MongoDB 会记录以下 4 个操作事件:
-
副本集、分片操作
-
身份验证、授权操作
-
DDL操作
-
CRUD操作
8.6.1 审计的启用与配置
mongoDB企业版中开启审计功能,需要在配文件中设定 auditLog 选项,格式如下:
auditLog:
destination: <string>
format: <string>
path: <string>
filter: <string>
配置auditLog 选项的四个参数说明如下。
(1)destination:表示审计的呈现方式。在 MongoDB 中可以选择以下3种不同的呈现方式。
- syslog:将操作事件以JSON 格式写入系统日志中。目前这个方式在 Windows 系统上暂不支持。
- console:直接将操作事件呈现在操作系统的命令提示行中。但此服务无法在后台运行(不能使用"--fork"参数)
- file:使用文件的形式记录操作事件,须配置文件路径(path )及输出形式(format)。
(2)format:输出操作事件的文件格式。当 destination 设定为file 时,需设定为JSON 或BSON格式。
(3)path:操作事件的文件路径。当 destination 被设定为file 时,需设定文件的绝对路径或相对路径。
(4)filter:针对指定的操作行为进行操作事件记录。在执行此操作时需设置"atype"和"param.db"两个
参数,这两个参数分别表示"指定操作"和"指定数据库"。若未指定,则默认记录所有审计事件的操作。
8.6.2 审计事件与过滤
MongoDB 支持许多操作的审计。当审计功能开启时,默认会将所有审计的操作事件记录下来,也可
以特别指定记录某些操作事件(只需在 MongoDB 命令启动时设置 auditFilter,或者在配置文件中设置
auditLog.filter)。下面将结合实际操作来介绍如何进行过滤。
配置文件范例如下:
auditLog:
# 将操作事件以文件形式输出
destination: file
# 文件格式是BSON格式
format: BSON
# 文件存放的路径
path: /var/log/mongodb/auditlog.bson
# 针对"mall"数据库的"身份验证"操作进行审计
filter: {atype:"authenticate","param.db":"mall"}
配置完成后重启数据库,然后验证一下是否有日志产生。
在配置文件中指定的/var/log/mongob/路经下,使用 "bsondump”令打开指定的审计文件
“auditlog.bson”,即可查看记录了身份验证的操作。
具体命令如下:
/var/log/mongodb# bsondump auditlog.bson
显示的结果如下:
{
"atype":"authenticate",
"ts":{"$date":"2024-09-06T18:09:46.533Z"},
"local":{"ip":"127.0.0.1","port':27017},
"remote":{"ip":"127.0.0.1","port":55733},
"users":[
{"user": "test","db":"mall"}
],
"roles":[
{"role":"readWrite", "db": "mall"}
],
"param": {"user":"test", "db":"mall","mechanism":"SCRAM-SHA-1"),
"result":0
}
2024-09-06T18:44:21.129+0800 1 objects found
8.7 检测安全漏洞
在所有环境都配置好之后,如果想知道环境是否安全,则可以通过一些开源软件来检测环境,防止
全漏洞。
1. 审计MongoDB 服务器工具 - mongoaudit
mongoaudit 可以检测 MongoDB 错误配置、已知的漏洞和错误。除此之外,它也提供了如何
修复MongoDB的建议。
此工具为开源,可在GitHub 下载,网址为: https://github.com/stampery/mongoaudit。
mongoaudit 工具可以做以下检测:
- MongoDB 监听的端口是否与默认端口不同。
- 是否已禁用MongoDB的HTTP接口。
- 是否已启用TLS/SSL加密。
- 是否已启用身份验证。
- 是否已启用 SCRAM-SHA-1 身份验证方法。
- 是否已禁止服务器端的Javascript 代码。
- 授予用户的角色是否只允许 CRUD 操作。
- 用户是否只拥有单个数据库的权限。
- 该服务器是否容易受到十几种不同的已知安全漏洞的攻击。
2.检测MongoDB 是否暴露在网络上
Shodan(https://www.shodan.io/)是一个搜索引整。用户可以通过关健词在 Shodan上查找放置
在外网的MongoDB 服务器的公开信息。
目前所有大规楼的 MongoDB 信息外泄,几乎都来自通过 Shodan 搜索引擎可以查寻到的服务器,
这些服务器因为 MongoDB 管理人员未完善安全配置而被攻击,通过 Shodan,用户可以确认自身的
MongoDB服务器是否暴露在Internet上。Shodan 搜索页面如图8-6所示。
为了降低 MongoDB 数据库系统的风险,须确保 MongoDB 运行在受信任的网络环境中,并限制连机
访问MongoDB 的接口。若只允许受信任的客户端可访问 MongoDB 的网络接口和端口,则可以通过
以下两种方式来实现。
- 通过配置防火墙来控制访问 MongoDB 服务器。
- 使用VPN,通过加密且受限访问的可信网络对 MongoDB 数据库进行访问。
到这里各位小伙伴们已经知道了许多数据库安全管理的概念及方法,在实际
操作中可以依照不同的需求选择不同的管理方式。