在上一篇文章 在MySQL中实现行级的访问控制,讲述了如何用视图和存储函数来实现基于角色的行级访问控制的环境配置。此篇文章我来实际测试此方法的效果。
9.1. 测试
9.1.1 各种权限用户可以访问对应标签的记录
9.1.1 用户sys1,属于角色SYS,定义为系统管理员,具有访问所有数据的权限。
mysql -u sys1 -p --execute="SELECT security_label,count(*) FROM protected.employee group by security_label"

9.1.2 用户dba1,属于角色DBA,定义为数据库管理员,具有访问除特级机密(TOP SECRET)之外数据的权限。
mysql -u dba1 -p --execute="SELECT security_label,count(*) FROM protected.employee group by security_label"

9.1.3 用户dev1,属于角色DEV,定义为数据库应用程序开发者,具有访问秘密(CONFIDENTIAL )数据和未归类(UNCLASSIFIED)数据的权限。
mysql -u dev1 -p --execute="SELECT security_label,count(*) FROM protected.employee group by security_label"

9.1.4 用户public1,属于角色PUBLIC,定义为公众人员,具有访问未归类(UNCLASSIFIED)权限数据的权限。
mysql -u public1 -p --execute="SELECT security_label,count(*) FROM protected.employee group by security_label"

9.1.2 异常测试一,访问没有权限的记录
9.1.2.1 用户dba1,属于角色DBA,尝试访问特级机密(TOP SECRET)数据。
mysql -u dba1 -p --execute="SELECT * FROM protected.employee where security_label='TOP SECRET'"
没有返回任何数据
9.1.2.2 用户dev1,属于角色DEV,尝试访问特级机密(TOP SECRET)数据和机密(SECRET)数据。
mysql -u dev1 -p --execute="SELECT * FROM protected.employee where security_label in ('TOP SECRET','SECRET')"
没有返回任何数据
9.1.2.3 用户public1,属于角色PUBLIC,尝试访问未归类(UNCLASSIFIED)权限之外的数据。
mysql -u public1 -p --execute="SELECT * FROM protected.employee where security_label !='UNCLASSIFIED'"
没有返回任何数据
9.1.3 异常测试二,访问未保护的原始数据
9.1.3.1 sys1访问test.employee,

9.1.3.2 dba1访问test.employee,

9.1.3.3 dev1访问test.employee,

9.1.3.4 public1访问test.employee,

9.1.4 异常测试三,
创建新用户abc,授予其访问protected schema select权限。但是没有为其指定角色,因此没有权限访问保护的数据。
CREATE USER 'abc'@'localhost' IDENTIFIED BY '123';
GRANT SELECT ON protected.* TO 'abc'@'localhost';
mysql -u abc -p --execute="SELECT * FROM protected.employee "
没有返回任何数据
9.1.5 增删改权限
9.1.5.1 sys1拥有 protected schema中的INSERT, UPDATE, DELETE权限
Sys1有权限插入一条TOP SECRET数据。
INSERT INTO protected.employee() VALUES(66,'Alice66', 5,20000, 'TOP SECRET');

Sys1有权限更新一条TOP SECRET数据。
UPDATE protected.employee SET payrank=30000 where empno=66

Sys1有权限删除一条TOP SECRET数据。
DELETE FROM protected.employee where empno=66
同理,Sys1有权限增删改其他安全标签(SECRET\CONFIDENTIAL\UNCLASSIFIED)的数据。
9.1.5.2 dba1拥有 protected schema中的INSERT, UPDATE, DELETE权限,但是增删改权限不适用于TOP SECRET级别记录。
Dba1没有权限增删改TOP SECRET级别记录。
I. 插入
INSERT INTO protected.employee() VALUES(66,'Alice66', 5,20000, 'TOP SECRET')

II. 更新
UPDATE protected.employee SET payrank=35000 where empno=38 and security_label='TOP SECRET';

III 删除

Dba1有权限增删改安全标签(SECRET)的记录。
INSERT INTO protected.employee() VALUES(66,'Alice66', 5,20000, 'SECRET')

同理,dba1有权限增删改其他安全标签(CONFIDENTIAL\UNCLASSIFIED)的数据。
类似地, dev1拥有 protected schema中的INSERT, UPDATE权限,但是增改权限不适用于TOP SECRET、SECRET级别记录。
Dev1有权限增改安全标签(CONFIDENTIAL,UNCLASSIFIED)的记录,没有权限删除(CONFIDENTIAL,UNCLASSIFIED)级别的记录。
bin/mysql -u dev1 -p --execute="INSERT INTO protected.employee() VALUES(66,'Alice66', 5,20000, 'CONFIDENTIAL')"
未定义的public角色的用户 public1没有 protected schema中的任何增删改权限。
在此方案中,使用了 non-deterministic存储函数作为行级安全策略,这种方法限制了优化器的权利去做优化查询。选择使用存储函数是因为它可以代码重用,所有需要保护的表可以重复使用。如果策略需要改变,只需要修改一个地方。
在测试环境中,表数据只有50行,性能影响不大。但是在实际环境中,这个策略必将造成查询性能的退步。如果性能不能被接受,可以直接将行级安全策略直接放在视图中实现,无需用存储函数。类似的实现如下,
CREATE SQL SECURITY DEFINER VIEW protected.vip
AS
SELECT unv.*
FROM unprotected.vip unv, accesses.role_accesses ra,accesses.sec_labels asl
WHERE asl.label_value & ra.label_value = asl.label_value and asl.security_label = unv.security_label and ra.user = getRole()
WITH CHECK OPTION;
结论
虽然MySQL 8.0没有内置的行级安全机制,但利用如上策略中的视图、存储函数等内置功能来实现还是相当容易的。此外,此策略是基于用户所属的角色权限来实现的。当数据库实例中存在大量的库用户时,权限管理将会变得越来越繁琐,可能要频繁进行权限变更。我基于MySQL 8.0的 role 功能,使得权限管理更加方便。在此实例中把用户组分为四个角色组SYS/DBA/DEV/PUBLIC,分别对应系统管理员、数据库管理员、应用程序开发者、公开人员。当需要对重要数据按行级访问控制时,可以直接为多个数据库用户授予相应 role 的权限即可。权限需要变更时,不需要每个用户一个一个的去变更,直接更改所属角色,从而实现方便运维和管理。 试一下吧!




