MSSQL-最佳实践-行级别安全解决方案
title: MSSQL-最佳实践-行级别安全解决方案
author: 风移
摘要
在SQL Server安全系列专题月报分享中,我们已经分享了:如何使用对称密钥实现SQL Server列加密技术、使用非对称密钥加密方式实现SQL Server列加密、使用混合密钥实现SQL Server列加密技术和列加密技术带来的查询性能问题以及相应解决方案四篇文章。本期月报我们分享使用SQL Server RLS(Row Level Security)行级别访问控制解决方案最佳实践。
问题引入
在很久以前我分享过一篇文章SQL Server使用视图做权限控制来实现行级别数据安全。今天我们把这个问题再次抛出来:不同用户访问同一张表,如何做到不同用户仅能访问属于自己及以下层级的数据。还是举例这个例子,比如:公司有CEO,Manger和普通的employee三种角色,CEO可以查看CEO、Manager和employee层级的数据;Manger只能查看Manger和employee的数据,不能查看CEO层级;而employee只能查看employee的数据,不能查看CEO和Manager级别的数据。今天我们把场景更进一步,要求用户仅能操作(DML)自己及下属的数据,不能跨级操作上层级的数据。
原理分析
SQL Server 2016版本引入了Row Level Security(以下简称RLS)新特性。这个特性本身不会对数据库中表数据做任何的加密和解密操作,而是基于用户定义的安全策略来限制和过滤表中行级别的数据,使得数据库引擎不会过多的暴露数据行,从而实现了非常简洁的访问控制方法,对用户的应用和客户端完全透明。因此,可以在用户不做任何代码修改情况下,简单两个步骤就可以轻松实现表行级别的数据安全访问控制。
创建RLS过滤函数:用于控制哪些用户可以查看哪些数据的逻辑控制
创建表级别的安全策略:用于实现表中数据行级别的安全访问控制
实现方法
按照问题引入部分的要求,我们需要实现数据查询访问控制和数据操作访问控制两个方面,以下是详细的实现方法步骤。
测试环境准备
还是沿用之前文章的测试场景数据,构建测试环境如下:
-- Create Test Database
IF DB_ID('Test') IS NULLCREATE DATABASE Test;
GOUSE Test
GO--create three logins(CEO, manager, employee)--create login CEO
IF EXISTS(SELECT *FROM sys.sysloginsWHERE name = 'CEO')
BEGINDROP LOGIN CEO;
END
GO
CREATE LOGIN CEO with password='CEODbo',check_policy = off;
GO--create user CEO
IF USER_ID('CEO') is not nullDROP USER CEO;
GO
CREATE USER CEO FOR LOGIN CEO;
GO--create login Manager
IF EXISTS(SELECT *FROM sys.sysloginsWHERE name = 'Manager')
BEGINDROP LOGIN Manager;
END
GO
CREATE LOGIN Manager with password='ManagerDbo',check_policy = off;
GO--create user manager
IF USER_ID('Manager') is not nullDROP USER Manager;
GO
CREATE USER Manager FOR LOGIN Manager;
GO--create login employee
IF EXISTS(SELECT *FROM sys.sysloginsWHERE name = 'employee')
BEGINDROP LOGIN employee;
END
GO
CREATE LOGIN employee with password='employeeDbo',check_policy = off;
GO--create user employee
IF USER_ID('employee') is not nullDROP USER employee
GO
CREATE USER employee FOR LOGIN employee;
GO--create basic TABLE
IF OBJECT_ID('dbo.tb_Test_ViewPermission','u')is not nullDROP TABLE dbo.tb_Test_ViewPermission
;
GO
CREATE TABLE dbo.tb_Test_ViewPermission
(id int identity(1,1) not null primary key,name varchar(20) not null,level_no int not null,title varchar(20) null,viewByCEO char(1) not null,viewByManager char(1) not null,viewByEmployee char(1) not null,salary decimal(9,2) not null
);--data init.
INSERT INTO dbo.tb_Test_ViewPermission
SELECT 'AA',0,'CEO','Y','N','N',1000000.0
union all
SELECT 'BB',1,'Manager','Y','Y','N',100000.0
union all
SELECT 'CC',2,'employee','Y','Y','Y',10000.0
;
GOselect * from dbo.tb_Test_ViewPermission在没做权限控制的情况下,不论是CEO,Manger还是Employee用户,都能够看到所有的数据,如下:
如此,无法做到表tb_Test_ViewPermission行级别的数据安全,也无法满足我们对数据行级别的查询和操作访问控制要求。
数据查询访问控制
让我们来看看如何实现行级别数据查询安全访问控制,需要实现如下三步:
建立RLS过滤函数
建立表级安全策略
验证查询访问控制
建立RLS过滤函数
首先,建立RLS过滤函数,用于实现哪些用户可以查看哪些数据的访问控制逻辑,实现代码如下:
USE Test
GO
CREATE SCHEMA RLSFilterDemo;
GO-- Create filter title function
CREATE FUNCTION RLSFilterDemo.fn_getTitle(@title AS varchar(20)) RETURNS TABLE
WITH SCHEMABINDING
AS
RETURNSELECT 1 AS result WHERE USER_NAME() IN (SELECT A.title FROM dbo.tb_Test_ViewPermission AS AINNER JOIN dbo.tb_Test_ViewPermission AS BON a.level_no <= B.level_noWHERE B.title = @title)
GO稍微解释下代码实现:使用level_no来控制用户访问数据的层级,level_no值越小,层级越高,权限越大。即:level_no为0(对应CEO用户)可以查看level_no为0(对应CEO自己)、1(对应Manger用户)和2(对应Employee普通用户);level_no为1的能查看自己和Employee的数据;而level_no为2的仅能查看自己的数据行。当我们发现查询的用户和对应的title匹配的时候,我们就认为这个用户有相应的权限,即函数返回值为1,反之,则认为没有权限访问对应的行。
建立表级安全策略
接下来,我们基于前面的过滤函数建立表级别安全策略,并且使得这个安全策略了生效,代码如下:
USE Test
GO
-- create security policy base on the filter function
CREATE SECURITY POLICY TitleFilter
ADD FILTER PREDICATE RLSFilterDemo.fn_getTitle(title)
ON dbo.tb_Test_ViewPermission
WITH (STATE = ON);
GO验证查询访问控制
最后一步,我们需要对行级别安全的访问控制进行查询验证和测试:
USE Test
GO
-- grant permissions to three users.
GRANT SELECT ON dbo.tb_Test_ViewPermission TO CEO;
GRANT SELECT ON dbo.tb_Test_ViewPermission TO Manager;
GRANT SELECT ON dbo.tb_Test_ViewPermission TO employee; USE Test
GO--CEO can read all of the data
EXECUTE AS USER='CEO'
SELECT WhoAmI = USER_NAME()
SELECT * FROM dbo.tb_Test_ViewPermission
REVERT;
GOUSE Test
GO
--Manager can read manager and employee's data, but except CEO's.
EXECUTE AS USER='Manager'
SELECT WhoAmI = USER_NAME()
SELECT * FROM dbo.tb_Test_ViewPermission
REVERT;
GOUSE Test
GO
--employee just can read employee's data, couldn't query CEO and Manger's.
EXECUTE AS USER='employee'
SELECT WhoAmI = USER_NAME()
SELECT * FROM dbo.tb_Test_ViewPermission
REVERT;
GO结果展示如下图所示:
从截图来看,CEO可以查看任何人的数据行;Manger可以查看自己和Employee的数据行;而Employee仅能查看自己的数据。说明已经成功实现了行级别的用户查询访问控制,达到了我们既定的目的。
数据操作访问控制
成功完成数据查询行级别访问控制的实践之后,我们再深入一步实现行级别数据操作访问控制。即:实现用户仅能操作自己及下层级的数据(level_no大于等于自己),而不能操作自己层级之上(level_no小于自己)的数据。如果,我们不做任何的访问控制,任何有权限的用户都可以操作我们这张表的数据,如下:
-- First, take the security policy off.
ALTER SECURITY POLICY TitleFilter
WITH (STATE = OFF);
-- Try to perform the DML action to see the DML permission control
USE Test
GO
-- grant permissions to all users.
GRANT UPDATE, INSERT, DELETE ON dbo.tb_Test_ViewPermission TO CEO;
GRANT UPDATE, INSERT, DELETE ON dbo.tb_Test_ViewPermission TO Manager;
GRANT UPDATE, INSERT, DELETE ON dbo.tb_Test_ViewPermission TO employee;USE Test
GO
--try to test INSERT by user employee
EXECUTE AS USER='employee'
SELECT WhoAmI = USER_NAME()INSERT INTO dbo.tb_Test_ViewPermission
SELECT 'DD',2,'employee','Y','Y','Y',100.0;
SELECT * FROM dbo.tb_Test_ViewPermission;UPDATE TOP(1) dbo.tb_Test_ViewPermission
SET name = 'EE'
WHERE name = 'DD';
SELECT * FROM dbo.tb_Test_ViewPermission;DELETE TOP (1)
FROM dbo.tb_Test_ViewPermission
WHERE name = 'EE'
;
SELECT * FROM dbo.tb_Test_ViewPermission;REVERT
GO
;执行以上语句后,我们发现,employee用户插入一条数据DD,然后更新为EE,接下来将这条数据删除了。展示截图如下:
说明在没有行级别安全访问控制的情况下,任何有权限的用户都可以操作这张表所有的数据,无法实现数据行级别安全。
建立RLS过滤函数
同样,首先建立RLS过滤函数,用于实现哪些用户可以操作哪些数据的访问控制逻辑:
-- Here we go to show how to allow manager and restrict employee dml operation
USE Test
GOCREATE SCHEMA RLSBlockDemo;
GOCREATE FUNCTION RLSBlockDemo.fn_getTitle(@title AS varchar(20))RETURNS TABLE
WITH SCHEMABINDING
ASRETURN SELECT 1 AS resultWHERE USER_NAME() IN (SELECT A.title FROM dbo.tb_Test_ViewPermission AS AINNER JOIN dbo.tb_Test_ViewPermission AS BON a.level_no <= B.level_noWHERE B.title = @title)
GO建立表级别安全策略
基于RLS过滤函数,建立表级别操作控制的安全策略并且使其启用生效:
USE Test
GO
ALTER SECURITY POLICY TitleFilter
ADD BLOCK PREDICATE RLSBlockDemo.fn_getTitle(title)
ON dbo.tb_Test_ViewPermission AFTER INSERT;ALTER SECURITY POLICY TitleFilter
WITH (STATE = ON);验证操作访问控制
接下来,就是表级别操作的访问控制验证了,Manger用户可以完全操作自己的数据:
USE Test
GO
--try to test INSERT by user Manager
EXECUTE AS USER='Manager'
SELECT WhoAmI = USER_NAME()INSERT INTO dbo.tb_Test_ViewPermission
SELECT 'DD',1,'Manager','Y','Y','Y',100.0;
SELECT * FROM dbo.tb_Test_ViewPermission;UPDATE TOP(1) dbo.tb_Test_ViewPermission
SET name = 'EE'
WHERE name = 'DD';
SELECT * FROM dbo.tb_Test_ViewPermission;DELETE TOP (1)
FROM dbo.tb_Test_ViewPermission
WHERE name = 'EE'
;
SELECT * FROM dbo.tb_Test_ViewPermission;REVERT
GOManger首先插入了一条DD数据,然后更新为EE,最后将其删除,整个过程没有报错,均成功执行,如下图所示:
同样,Manger也可以操作Employee数据,即自己层级以下的数据(level_no大于自己):
USE Test
GO
--It's OK to INSERT manger record by user Manager
EXECUTE AS USER='Manager'
SELECT WhoAmI = USER_NAME()INSERT INTO dbo.tb_Test_ViewPermission
SELECT 'EE',2,'employee','Y','Y','N',100.0;
SELECT * FROM dbo.tb_Test_ViewPermission;
REVERT
GO
;Manger插入了一条Employee的数据EE:
但是,Manger不能操作自己层级以上的数据(level_no小于自己),比如CEO的数据,如下代码:
USE Test
GO
--Failed to INSERT CEO record by user Manager
EXECUTE AS USER='Manager'
SELECT WhoAmI = USER_NAME()INSERT INTO dbo.tb_Test_ViewPermission
SELECT 'DD',0,'CEO','Y','Y','Y',100.0;REVERT
GO
;Manger试图操作CEO的数据,会报告如下错误:
(1 row affected)
Msg 33504, Level 16, State 1, Line 286
The attempted operation failed because the target object 'Test.dbo.tb_Test_ViewPermission' has a block predicate that conflicts with this operation. If the operation is performed on a view, the block predicate might be enforced on the underlying table. Modify the operation to target only the rows that are allowed by the block predicate.
The statement has been terminated.错误截图如下所示:
在完成数据行级别安全策略之后,我们可以成功实现用户数据行级别操作访问控制安全,达到我们既定安全目标。
最后总结
本期月报我们分享了使用SQL Server 2016引入的新特性 Row Level Security实现数据访问控制解决方案最佳实践,在用户无需对应用做任何改动的情况下实现表行级别数据查询和操作访问控制,使得最大限度保证表行级别数据安全。
相关文章:
浮点数运算原理详解
导读: 浮点数运算是一个非常有技术含量的话题,不太容易掌握。许多程序员都不清楚使用操作符比较float/double类型的话到底出现什么问题。 许多人使用float/double进行货币计算时经常会犯错。这篇文章是这一系列中的精华,所有的软件开发人员都…

vCenter的安装
转载于:https://blog.51cto.com/yht1990/1857211

【数据结构】双链表的应用
1、设计一个算法,在双链表中值为y的结点前面插入一个值为x的新结点,即使得值为x的新结点成为值为y的结点的前驱结点。 2、设计一个算法,将一个双链表改建成一个循环双链表。 #include <stdio.h> #include <stdlib.h>typedef st…

Eclipse for Tricore 的安装方法
1.安装JDK32位版 2.安装Eclipse for Tricore 32位版(应该也只有32位的) 3.OK(如果打开Tricore提示找不到JDK的话,在网上搜索如何配置JDK,修改环境变量) 注意:Eclipse的位数必须和JDK位数相同 转…
NDK JNI方式读写Android系统的demo(二)
NDK & JNI(方式读写Android系统的Demo) 大家都知道Android系统是一种基于Linux的自由及开放源码的操作系统,所以读写GPIO也可以直接用Linux那一套export/unexport方法,本文将介…

【数据结构】顺序串的插入算法,删除算法,连接运算,顺序串求子串算法
主函数自行添加 头文件 宏定义 #include <stdio.h> #include <stdlib.h> #define MAXSIZE 100 串的顺序存储 typedef struct {char str[MAXSIZE];int length; }seqstring; 顺序串的创建 void creat(seqstring *S) {char c;int i0;while((cgetchar())!\n){S-…

log4j日志记录级别是如何工作?
级别p的级别使用q,在记录日志请求时,如果p>q启用。这条规则是log4j的核心。它假设级别是有序的。对于标准级别它们关系如下:ALL < DEBUG < INFO < WARN < ERROR < FATAL < OFF。 举个栗子 下面的栗子明确指出如何可以过…

Kafka背后公司获1.25亿融资,估值超25亿美元
北京时间1月24日,开源Apache Kafka项目背后的公司Confluent在官方博客宣布进行了D轮融资,价值约为1.25亿美元,公司总估值高达25亿美元。 Confluent公司CEO Jay Kreps在博客中表示:我很高兴地宣布,Confluent已经募集了1…

深入学习jQuery描述文本内容的3个方法
前面的话 在javascript中,描述元素内容有5个属性,分别是innerHTML、outerHTML、innerText、outerText和textContent。这5个属性各自有各自的功能,且兼容性不同。jQuery针对这样的处理提供了3个便捷的方法,分别是:html(…
NDK JNI Android Studio开发与调试DEMO(三)(生成 .so 文件)
Android Studio NDK 开发与调试(生成 .so 文件) 温馨提示:如果你的 Android Studio 版本在 3.0以上 , 建议你用 cMake /ndk-build 的新姿势进行 NDK 开发 : https://developer.android.google.cn/index.html AS与g&am…
字符串的模式匹配 (朴素模式匹配算法 ,KMP算法)
字符串的模式匹配 寻找字符串p在字符串t中首次出现的起始位置 字符串的顺序存储 typedef struct {char str[MAXSIZE];int length; }seqstring; 朴素的模式匹配算法 基本思想:用p中的每一个字符去与t中的字符一一比较。 模式p 正文 t 如果匹配成功,…

框架页面jquery装载
转载于:https://www.cnblogs.com/moonsoft/p/10313309.html

NDKJNI Android 相关资料整理(四)
JNI/NDK开发指南 JNI/NDK开发指南(一)—— JNI开发流程及HelloWorld http://blog.csdn.net/xyang81/article/details/41777471 JNI/NDK开发指南(二)——JVM查找java native方法的规则 http://blog.csdn.net/xyang81/article/det…
【ACM】与全排列相关的STL函数 prev_permutation next_permutation
排列 与 全排列 从n个不同元素中任取m(m≤n)个元素,按照一定的顺序排列起来,叫做从n个不同元素中取出m个元素的一个排列。 当mn时所有的排列情况叫全排列。如果这组数有n个,那么全排列数为n!个。 对于全排列的求解…

虚拟机无法使用网卡桥接模式
重装了好几遍操作系统,换了两个虚拟机,差点怀疑人生…… 最终的原因竟然是win10!!! 1.执行WINR 输入services.msc,打开服务管理器(回车)。 2.找到Device Install Service服务 启动此…

Azure自动化部署运维浅谈
本次来谈一谈如何在Azure中实现一些简单的自动化运维的需求,一般来讲自动化运维我们通过很多第三方的工具平台实现,比较流行的目前有很多,比如老牌的chef, puppet,新兴的PowerShell DSC, ansible。这些应该都是耳熟能详的了。那么在Azure平台…
NDK/JNI demo ( 五 ) ORB_SLAM2在Android上的移植过程
Android平台搭建和NDK环境配置 Android移植基础 NDK是集成的Android中调用C代码的工具包,核心是JNI(Java Native Interface)技术,具体这里略过不表。只说说NDK开发的基本步骤: 1. 编写Java代码:在Java中定义…

[Nancy On .Net Core Docker] 轻量级的web框架
.net core现在已经有了大的发展,虽然笔者现在已经从事python开发,但是一直在关注.net的发展,在逛博客园的时候,发现有大家都会提到Nancy这个框架,在简单的使用之后,发现竟然是如此的简单而优雅 public clas…

【算法】【ACM】深入理解Dijkstra算法(单源最短路径算法)
Dijkstra算法是用来求解从某个源点到其他各顶点的最短路径(单源最短路径)。 下面的Dijkstra算法的讲解都是基于这个有向图,在遇到其他问题可以类比。 算法的基本思想: 把图中的定点分成两组,第一组包括已确定最短路径…

智能POS常见问题整理
智能POS预警值为小于所设的数量,H5就会变为锁定状态 智能POS查看数据库方法: 商米D1:设置-存储设备和USB-内部存储设备-浏览-winboxcash tablet.db为智能POS数据库 Winbox文件夹内,为相应logcat文件,应用出现问题时&am…
解决安卓系统写入SD卡权限问题
1.需要用户手动赋予的权限( Dangerous Permissions) 所属权限组权限日历READ_CALENDAR日历WRITE_CALENDAR相机CAMERA联系人READ_CONTACTS联系人WRITE_CONTACTS联系人GET_ACCOUNTS位置ACCESS_FINE_LOCATION位置ACCESS_COARSE_LOCATION麦克风RECORD_AUDIO…

【ACM】【STL】stack应用
C Stacks(堆栈) C Stack(堆栈) 是一个容器类的改编,为程序员提供了堆栈的全部功能,——也就是说实现了一个先进后出(FILO)的数据结构。 操作比较和分配堆栈empty()堆栈为空则返回真…

CHUCK手把手带你搞定OPENSTACK
以下是原文链接:http://blog.oldboyedu.com/openstack/转载于:https://blog.51cto.com/bovin/1858198

JVM基础面试题及原理讲解
2019独角兽企业重金招聘Python工程师标准>>> 本文从 JVM 结构入手,介绍了 Java 内存管理、对象创建、常量池等基础知识,对面试中 JVM 相关的基础题目进行了讲解。 写在前面(常见面试题) 基本问题 介绍下 Java 内存区域…
SLAM前端 ---------特征提取之ORB(ORB与SIFT与SURF)
ORB 论文翻译: 一种特征匹配替代方法:对比SIFT或SURF 1.ORB特征简介 ORB是Oriented FAST and Rotated BRIEF(oFAST and rBRIEF)的简称,ORB的名字已经说明了其来源,其实ORB特征是采用FAST方法来检测提取特…

oracle 内存分配和调优 总结
一直都想总结一下oracle内存调整方面的知识,最近正好优化一个数据库内存参数,查找一些资料并且google很多下。现在记录下来,做下备份。 一、概述: oracle 的内存可以按照共享和私有的角度分为系统全局区…

【ACM】Doubly Linked List(STL list)
题目链接:https://vjudge.net/problem/Aizu-ALDS1_3_C 这一题一开始的时候想的是用vector,超时 #include <iostream> #include <stack> #include <cstdio> #include <cstring> #include <queue> #include <vector>…

IOS获取焦点页面上移问题
var u navigator.userAgent; var flag; var myFunction; var isIOS !!u.match(/(i1;( U;)? CPU.Mac OS X/); if (isIOS) { document.body.addEventListener(focusin, () > { //软键盘弹起事件flag true;clearTimeout(myFunction); }) document.body.addEventListener(f…
SLAM之特征匹配(二)————RANSAC--------翻译以及经典RANSAC以及其相关的改进的算法小结
本文翻译自维基百科,英文原文地址是:http://en.wikipedia.org/wiki/ransac RANSAC是“RANdom SAmple Consensus(随机抽样一致)”的缩写。它可以从一组包含“局外点”的观测数据集中,通过迭代方式估计数学模型的参数…

【ACM】树 小结
树是一种表达层级结构的数据结构,也是实现高效算法与数据结构的基础。 学习之前的基础:数组,循环处理,结构体,递归函数。 树:由结点(node)和连接结点的边(edge…