暂无图片
暂无图片
暂无图片
暂无图片
暂无图片

mongo-06-数据库安全

黑桃 2021-01-20
507

数据库安全涉及到两个方面,一是身份认证,二是鉴权。

mongodb安装完后默认是不开启auth安全模块的,直接通过 mongo
工具或者其他工具不需要身份认证就可以成功连接mongo服务。

1 开启安全模式 --auth

为了安全起见,我们需要开启安全模式 --auth

[root@docker01 ~]# /usr/local/mongodb-4.4.3/bin/mongod -f /usr/local/mongodb-4.4.3/mongodb.conf --auth
about to fork child process, waiting until server is ready for connections.
forked process: 4195
child process started successfully, parent exiting
[root@docker01 ~]#

这时,我们连接来读取 test 数据库的集合,发现读取失败。

[root@docker01 ~]# /usr/local/mongodb-4.4.3/bin/mongo --host localhost --port 27017
MongoDB shell version v4.4.3
connecting to: mongodb://localhost:27017/?compressors=disabled&gssapiServiceName=mongodb
Implicit session: session { "id" : UUID("5106e9d5-4480-414b-b0b5-6c6b729028ab") }
MongoDB server version: 4.4.3
> use test
switched to db test
> show collections
Warning: unable to run listCollections, attempting to approximate collection names by parsing connectionStatus
>

MongoDB将所有角色信息存储在admin数据库的admin.system.roles集合中。一般,切换到admin数据库,创建角色,添加用户。

创建一个管理员用户 root,密码也是 root(创建用户细节下面详解)。

> use admin
switched to db admin
> db.createUser({ user: "root", pwd: "root", roles: [{ role: "userAdminAnyDatabase", db: "admin" }] })
Successfully added user: {
"user" : "root",
"roles" : [
{
"role" : "userAdminAnyDatabase",
"db" : "admin"
}
]
}
>

1.1 身份认证

方式一:
mongo -u <username> -p <password> --authenticationDatabase <db>

身份认证后,可以查看 test 数据库的集合

[root@docker01 ~]# /usr/local/mongodb-4.4.3/bin/mongo --host localhost --port 27017 -u root -p root --authenticationDatabase admin
MongoDB shell version v4.4.3
connecting to: mongodb://localhost:27017/?authSource=admin&compressors=disabled&gssapiServiceName=mongodb
Implicit session: session { "id" : UUID("89008f1d-7278-4387-9992-7ff1054a7d3f") }
MongoDB server version: 4.4.3
> use test
switched to db test
> show collections
outDoc
user
>

方式二:

不指定用户名密码连接上mongo,然后使用 db.auth("username","password")
;

[root@docker01 ~]# /usr/local/mongodb-4.4.3/bin/mongo --host localhost --port 27017
MongoDB shell version v4.4.3
connecting to: mongodb://localhost:27017/?compressors=disabled&gssapiServiceName=mongodb
Implicit session: session { "id" : UUID("3736b07f-4793-4dcb-8af8-ca48d9f508f7") }
MongoDB server version: 4.4.3
> use admin
switched to db admin
> db.auth("root","root")
1
> show collections
system.users
system.version
> show databases
admin 0.000GB
config 0.000GB
local 0.000GB
test 0.000GB
>

2. 权限和内置角色

2.1 权限

MongoDB的权限包:资源(Resource)和操作(Action)两部分组成。
权限:在哪里+做什么

资源:

  1. 集群级别:{ cluster: true }
    ;

  2. 数据库级别:{ db: "test", collection: "" }
    ;

  3. 表级别:{ db: "test", collection: "user" }
    ;

Privilege Actions 定义User能够在资源上执行的操作。
例如:MongoDB在文档级别上执行的读写操作列表是:find,insert,remove,update
;

2.2 mongo 内置的角色:

  1. 普通数据库用户的角色:

  • read:只读数据的权限

  • readWrite:读写数据的权限

  1. 跨库角色:除config和local之外所有的数据库:

  • readAnyDatabase:在所有数据库上读取数据的权限

  • readWriteAnyDatabase:在所有数据库上读写数据的权限

  • userAdminAnyDatabase:在所有数据库上管理User的权限

  • dbAdminAnyDatabase:管理所有数据库的权限

  1. 数据库管理角色:

  • userAdmin:在当前DB中管理User

  • dbAdmin:在当前dB中执行管理操作

  • dbOwner:在当前DB中执行任意操作

  1. 超级角色:

  • userAdmin

  • userAdminAnyDatabase

  • root

  1. 集群管理角色:

  • clusterMonitor:授予监控集群的权限,对监控工具具有readonly的权限

  • clusterAdmin:授予管理集群的最高权限

  • clusterManager:授予管理和监控集群的权限

  • hostManager:管理Server

  1. 备份恢复角色:

  • backup

  • restore

  1. 内部角色:

  • __system

  • __queryableBackup

  1. 分片角色:

  • enableSharding

3. 角色

3.1 创建角色

db.createRole(<role>, <writeConcern>)

<role>
 : 角色定义文档
<writeConcern>
 : 写安全级别

在创建角色时,必须明确Role的四个特性:

  • Scope
     :角色作用的范围,

  1. 创建在Admin中的角色,能够在其他DB中使用;在其他DB中创建的角色,只能在当前DB中使用;

  2. 在admin 数据库中创建的角色,Scope是全局的,能够在admin,其他DB和集群中使用,并且能够继承其他DB的Role;而在非admin中创建的角色,Scope是当前数据库,只能在当前DB中使用,只能继承当前数据库的角色。
    因此创建角色,一般都在admin库中创建。

  • Resource
     :角色控制的资源,表示授予在该资源上执行特定操作的权限;

  • Privilege Actions
     :定义了User能够在资源上执行的操作,系统定义Action是:Privilege Actions;

  • Inherit
     :角色能够继承其他角色权限;

role文档格式如下

{
role: "<name>", //角色名
privileges: [ //权限数组,包括资源和权限操作
{ resource: { <resource> }, actions: [ "<action>", ... ] },
...
],
roles: [//父类角色数组,对于该数据库角色可直接用role字符串表示
{ role: "<role>", db: "<database>" } | "<role>",
...
],
authenticationRestrictions: [ //认证限制数组,可选,确定一组可连接IP地址、CIDR范围
{
clientSource: ["<IP>" | "<CIDR range>", ...],
serverAddress: ["<IP>" | "<CIDR range>", ...]
},
...
]
}

创建一个针对 test 数据库user集合的只读角色 testRead

> use admin
> db.createRole(
... {
... role: "testRead",
... privileges: [
... { resource: { db: "test", collection: "user" }, actions: [ "find", "listCollections","listIndexes"] }
... ],
... roles: []
... }
... );
{
"role" : "testRead",
"privileges" : [
{
"resource" : {
"db" : "test",
"collection" : "user"
},
"actions" : [
"find",
"listCollections",
"listIndexes"
]
}
],
"roles" : [ ]
}
>

3.2 查看角色

查看所有角色

show roles

> show roles
{
"role" : "__queryableBackup",
"db" : "admin",
"isBuiltin" : true,
"roles" : [ ],
"inheritedRoles" : [ ]
}
...
...
>

查看具体的角色

db.getRole(<RoleName>)

查看内置的 read 角色

> db.getRole("read")
{
"role" : "read",
"db" : "admin",
"isBuiltin" : true,
"roles" : [ ],
"inheritedRoles" : [ ]
}
>

查看上面创建的 testRead 角色的具体信息 {showPrivileges: true}

> db.getRole("testRead",{showPrivileges: true})
{
"role" : "testRead",
"db" : "admin",
"isBuiltin" : false,
"roles" : [ ],
"inheritedRoles" : [ ],
"privileges" : [
{
"resource" : {
"db" : "test",
"collection" : "user"
},
"actions" : [
"find",
"listCollections",
"listIndexes"
]
}
],
"inheritedPrivileges" : [
{
"resource" : {
"db" : "test",
"collection" : "user"
},
"actions" : [
"find",
"listCollections",
"listIndexes"
]
}
]
}
>

3.3 修改角色

db.updateRole(<roleName>, <roleDocument>)

将 testRead 角色修改为针对整个test数据库只读

> db.updateRole("testRead",
... {
... privileges: [
... { resource: { db: "test", collection: "" }, actions: [ "find", "listCollections","listIndexes"] }

... ],
... roles: []
... }
... );
>

查看修改后 testRead 角色的详细信息

> db.getRole("testRead",{showPrivileges: true})
{
"role" : "testRead",
"db" : "admin",
"isBuiltin" : false,
"roles" : [ ],
"inheritedRoles" : [ ],
"privileges" : [
{
"resource" : {
"db" : "test",
"collection" : ""
},
"actions" : [
"find",
"listCollections",
"listIndexes"
]
}
],
"inheritedPrivileges" : [
{
"resource" : {
"db" : "test",
"collection" : ""
},
"actions" : [
"find",
"listCollections",
"listIndexes"
]
}
]
}
>

3.4 删除角色

db.dropRole("roleName")

创建test数据库的写角色 testWrite,然后删除 testWrite 角色,再次查看,返回null,表示角色不存在

#使用管理员 root 用户进行操作
> db.auth("root","root")
1
# 创建角色 testWrite
> db.createRole(
... {
... role: "testWrite",
... privileges: [
... { resource: { db: "test", collection: "" }, actions: [ "insert", "update","remove"] }
... ],
... roles: []
... }
... );
{
"role" : "testWrite",
"privileges" : [
{
"resource" : {
"db" : "test",
"collection" : ""
},
"actions" : [
"insert",
"update",
"remove"
]
}
],
"roles" : [ ]
}
# 查看 testWrite 角色
> db.getRole("testWrite")
{
"role" : "testWrite",
"db" : "admin",
"isBuiltin" : false,
"roles" : [ ],
"inheritedRoles" : [ ]
}
# 删除 testWrite 角色
> db.dropRole("testWrite")
true
# 再次查看,返回null,表示角色不存在
> db.getRole("testWrite")
null
>

4. 创建用户

db.createUser(<user>, <writeConcern>)

<user>
 : 验证和访问信息
<writeConcern>
 : 安全写级别

user文档格式如下:

{
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>", ... ], //指定用于创建SCRAM用户凭据的特定SCRAM机制,可选。3.6默认SCRAM-SHA-1
passwordDigestor: "<server|client>" //密码摘要,可选,指定用户端/服务器是否生成密码摘要
}

使用上面的 testRead角色,创建一个针对test数据库的只读用户, 名字叫做 testRead,密码也是 testRead。

> user admin
> db.createUser({ user: "testRead", pwd: "testRead", roles: [{ role: "testRead", db: "admin" }] })
Successfully added user: {
"user" : "testRead",
"roles" : [
{
"role" : "testRead",
"db" : "admin"
}
]
}

对上面创建的 testRead 用户进行身份认证,然后查看test数据库集合,查看user集合内容。因为只有读权限,所以插入文档时,报错"Unauthorized",未授权。

# 身份认证
> db.auth("testRead","testRead")
1
> use test
switched to db test
# 查看数据库中的集合
> show collections
outDoc
user
# 可以查看user集合内容
> db.user.find()
{ "_id" : "1", "money" : 1500, "name" : "刘一一", "gender" : "女" }
{ "_id" : "2", "money" : 1000, "name" : "陈二", "gender" : "女" }
{ "_id" : "3", "name" : "张三", "sal" : 1000, "gender" : "女" }
{ "_id" : "4", "money" : 800, "name" : "李四", "gender" : "女" }
{ "_id" : "5", "money" : 1000, "name" : "王五", "sal" : 500, "gender" : "女" }
{ "_id" : "6", "name" : "赵六", "money" : 1000 }
{ "_id" : "7", "money" : 1000, "name" : "孙七", "gender" : "女" }
{ "_id" : ObjectId("5fff6a78025e5d9ea86e18cc"), "money" : 1000, "name" : "周八", "gender" : "女" }
{ "_id" : { "age" : 18, "gender" : "男" }, "money" : 1000, "name" : "吴九", "gender" : "女" }
{ "_id" : ObjectId("5fff95783f6d5ed2fcce4133"), "money" : 2000, "name" : "ZHANG", "addr" : [ "三亚", "北京" ], "gender" : "女" }
{ "_id" : ObjectId("60045f5719414c7b5be4c122"), "name" : "富二代", "money" : 1500 }
{ "_id" : ObjectId("6004611c19414c7b5be4c13f"), "name" : "打工人", "money" : 800 }
{ "_id" : ObjectId("6004c82e21bca04a14f52bee"), "money" : 1100 }
{ "_id" : ObjectId("6004ce5521bca04a14f52bf0"), "money" : 1200 }
{ "_id" : ObjectId("6004ce8f21bca04a14f52bf1"), "money" : 1300, "name" : null }
# 插入文档失败,未授权
> db.user.insert({name:"只读用户"})
WriteCommandError({
"ok" : 0,
"errmsg" : "not authorized on test to execute command { insert: \"user\", ordered: true, lsid: { id: UUID(\"3736b07f-4793-4dcb-8af8-ca48d9f508f7\") }, $db: \"test\" }",
"code" : 13,
"codeName" : "Unauthorized"
})
>

[CSDN同步:mongo-06-数据库安全] https://blog.csdn.net/qq_20633779/article/details/112911386
[慕课手记同步:mongo-06-数据库安全] https://www.imooc.com/article/314521


欢迎关注文章同步公众号"黑桃"


文章转载自黑桃,如果涉嫌侵权,请发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

评论