本篇文章是SQL Server安全系列的第四篇,详细内容请参考原文。
权限授予主体访问对象,以执行某些操作。SQL Server有大量你可以授予给主体的权限,你甚至可以拒绝或回收权限。这听起来有点复杂,但在这一系列,你将知道SQL Server权限是如何工作的,你可以非常精细地在数据库和服务器对象上控制对象创建、数据访问、以及其他类型操作。
权限
权限像一个签证允许你访问外国,通常有一些基本条件。比如,你只有六个月的期限,你被限制在3/7的地区旅行。类似的,SQL Server权限给主体访问数据库对象做一些事或执行操作。权限可以允许主体读取表的数据或表的某一部分,或能够运行一段特定的代码。甚至允许主体给其他登录名授予权限。你可以给不同的主体授予数百种不同的权限。
在授予权限时,你要遵循最小特权原则。最小特权意味着,你给一个主体需要完成一个任务的权限——没有多也没有少。坚持最小特权原则,是数据库安全的重要步骤。如果主体在数据库中唯一能做的事情是读取产品信息,这个主体就不应该能有意或无意删除表中的内容。本质上是建立一个严密的容器限制主体可以做什么。
权限类型
SQL Server有许多种你可以授予主体控制访问安全对象的权限。下面列出最常见的权限:
控制:赋予安全对象的所有可能的权限,使主体成为安全对象的虚拟拥有者。这包括将安全对象授予权限给其他主体。
创建:赋予创建一个特定对象的能力,这取决于它被授予的范围。例如,CREATE DATABASE权限允许主体在SQL Server实例创建新的数据库。
更改:赋予更改安全对象属性的权限,除了更改所有者。这个权限包含了同范围的alter、create、drop对象的权限(比如,用户对表A有更改权限,那么它能够add/alter/drop列、create/drop索引约束等)例如,一个数据库级别的更改权限,包括更改表和架构权限。
删除:允许一个主体删除任何或所有存储在表中的数据。是一个非常危险的权限!
模拟<登录>或者模拟<用户名>:赋予主体模拟另一个登录/用户。通常用于改变存储过程的执行上下文。
插入:允许主体往表中插入新记录
选择:授予主体从一个特定的表读取数据。这是用户需要的最常见的权限,以便他们可以在表上执行查询。
接管所有权:Confers on a principal permission to take ownership of an object. Granting this permission does not immediately transfer ownership. Instead, it allows the principal to take ownership at some future time.
更新:允许主体更新表中的数据。
查看定义:赋予主体权限查看安全对象的定义。这是一个重要的权限,因为结构信息在数据库攻击中是非常有用的。如果没有这个权限,攻击者发现数据库或服务器实例有价值目标的能力将严重受限。
创建和更改权限可以使用ANY关键字:CREATE ANY <object type> and ALTER ANY <object type>.这些权限授予创建或更改任何指定类型安全对象。例如,在数据库级别上授予ALTER ANY SCHEMA,允许改变数据库中任何架构的属性。在服务器级别上,ALTER ANY LOGIN允许主体改变服务器上任何登录名。使用ANY关键字,可以灵活的让主体创建或修改一整类的对象,而不是一个单一的对象。只是要注意,在创建和修改权限之间有一些细微的差别。
当你考虑在SQL Server安全对象的数量和一个对象可以拥有潜在权限类型的数量时,你开始意识到有颗粒的权限。你可以应用最小特权原则来实现任何用户或角色的权限集,通过给予用户恰当的权限来完成一项任务,而不需要将其他对象暴露。
提示:如果你使用SQL Server早期版本,你会意识到,SQL Server 2005及之后版本完全修改了可用的权限。你不必再将一个用户分配给一个可能有几十个不必要的权限的角色,从而违反了最小特权,并将数据库暴露在有意或偶然的滥用。
这里一个主要考虑是,授予一个权限不一定有效地授予执行一项操作的能力。有时还需要其他权限,提供一个全面的安全上下文来执行敏感操作的能力。一个很常见的例子是创建表权限。授予这个权限,理论上允许一个主体在特定的数据库下创建表。但主体还需要有能力在一个架构中创建表。如果主体没有对任何架构的更改权限,它将无法创建一个表。
权限语句
即使你是通过图形化工具分配权限,底层还是执行TSQL权限语句。
GRANT:授予对安全对象或操作的权限给主体
REVOKE:“Un-grant”权限,取消一个早期做的grant语句。撤销的权限仍然可以通过在具有权限的组或角色中继承。当你创建新的对象时,revoke是默认的权限状态,所以特定权限没有授予,但是可以继承。
DENY:拒绝撤销权限,使它不能被继承。这是最具限制性的权限,并且优先于所有其他权限。DENY不适用于sysadmin角色的成员或对象的所有者。
不要低估DENY权限的重要性。例如,假设你有一个临时雇员,他是进来做数据输入,你不希望他能够编辑或删除现有记录。你已经分配给editor角色(公司内部人员都属于)对某些表的足够的权限。你可以创建一个特殊的登录名,然后在适当的表上deny UPDATE和DELETE权限。临时雇员可以从editor角色继承足够的权限输入新记录,但是不能修改/删除已存在的记录。
授予权限
在Management Studio下授予权限的最灵活的方法是修改数据库用户或角色的属性。你还可以通过修改单个对象的属性来授予权限,但这种方法不灵活,并且具有更高的维护成本。
下面的练习将在AdventureWorks2012数据库创建自定义数据库角色。这个角色的成员,需要必要的权限插入和更新一些HR相关的表,并且能够执行相关的存储过程,但没有其他的特殊权限。
提示:一旦你为一个用户配置权限,然后需要为另一个用户重复上述过程,授予和第一个用户同样的权限,那么把权限分配给一个角色,然后简单地将用户添加到角色即可。
你可以利用第二篇在AdventureWorks2012数据库下创建的用户Topaz。如果你没有创建用户,执行代码4.1创建登录名和用户。
USE master;
GO
CREATE LOGIN Topaz WITH PASSWORD = 'yBqyZIPT8}b]b[{5al0v';
GO
USE AdventureWorks2012;
GO
CREATE USER Topaz FOR LOGIN Topaz
WITH DEFAULT_SCHEMA = HumanResources;
GO
代码4.1 创建创建名并映射到数据库用户
在AdventureWorks2012数据库创建一个DataEntry自定义数据库角色,参考如下步骤:
1、SSMS连接到安装有AdventureWorks2012数据库的实例。对象资源管理器->数据库->AdventureWorks2012->安全性->角色->数据库角色
2、右击数据库角色,弹出菜单选择新建数据库角色
3、角色名称键入DataEntry,所有者dbo
4、点击添加按钮,将用户Topaz添加到角色(如果用户不存在请先创建)。在你关闭选择数据库用户或角色对话框后,数据库角色-新建对话框应该如图4.1所示
图4.1 创建DataEntry数据库角色并添加Topaz用户
5、点击确定保存修改并创建角色
当前DataEntry角色并不允许角色中的成员能做什么,因为你还没有给角色分配任何权限。DataEntry角色中的成员需要能够往表Employee、Address、JobCandidate插入和更新数据,同时他们需要执行uspUpdateEmployeeHireInfo和uspUpdateEmployeePersonaInfo存储过程。但是他们不能查看存储过程的定义。
使用下面的步骤给DataEntry角色添加合适的权限
1、对象资源管理器->数据库->AdventureWorks2012->安全性->角色->数据库角色->DataEntry
2、右击DataEntry,弹出菜单选择属性。打开数据库角色属性对话框
3、左侧选择安全对象,这个页面让你选择角色有权操作的安全对象,并指定对安全对象上的权限
4、点击“搜索”按钮添加安全对象。这将打开“添加对象”对话框,该对话框提供了特定对象、特定类型的所有对象、属于该架构的所有对象的选项。在本例中,因为你想为表和存储过程中添加权限,图4.2中保持默认选择-特定对象,然后单击“确定”
图4.2 选择添加对象
5、打开“选择对象”对话框。单击“对象类型”按钮打开“选择对象类型”对话框,然后从列表中选择“存储过程”和“表”,如图4.3所示。请单击“确定”以关闭该对话框并返回“选择对象”对话框,该对话框现在看起来像图4.4。你将看到在对象类型框中列出的存储过程和表
图4.3 选择对象类型
图4.4 选择对象
6、单击“浏览”按钮以查看数据库中的存储过程和表的列表。这将打开“查找对象”对话框,向下滚动查找并选择对象。
图4.5 选择存储过程和表
7、单击“确定”以关闭“查找对象”对话框中。此时你选择的对象以分号分隔列在选择对象对话框中,如图4.6所示。请单击“确定”以关闭该对话框并保存更改
图4.6 选择对象结果
8、现在DataEntry数据库角色属性对话框列出你选择的安全对象,并且列出每个对象可用的权限。DataEntry角色成员需要可以插入和更新数据到这些表,逐一选择表,在对话框的下部勾选插入/更新授予列的复选框。图4.7显示了HumanResources.Employee表的权限
图4.7 在表上授予插入/更新权限
提示:授予(Grant)允许指定的权限,具有授予权限(With Grant)允许用户或角色授予其他主体的权限。小心With Grant权限!
9、对于每一个存储过程,授予执行权限并拒绝查看定义权限。图4.8显示了useUpdateEmployeeHireInfo存储过程的权限
图4.8 授予执行过程权限、拒绝查看定义权限
10、在“数据库角色属性”对话框中单击“确定”以保存你的更改并提交到数据库。根据选定的对象和权限的数量,这可能需要一些时间。
当然,你可以使用T-SQL代码创建对象并分配权限。代码4.2创建DataEntry角色,添加Topaz用户,然后分配和图形界面相同的权限。
-- Create the DataEntry role and assign Topaz to it
CREATE ROLE [DataEntry] AUTHORIZATION [dbo];
ALTER ROLE [DataEntry] ADD MEMBER [Topaz];
-- Assign permissions to the DataEntry role
GRANT INSERT ON [HumanResources].[Employee] TO [DataEntry];
GRANT UPDATE ON [HumanResources].[Employee] TO [DataEntry];
GRANT INSERT ON [HumanResources].[JobCandidate] TO [DataEntry];
GRANT UPDATE ON [HumanResources].[JobCandidate] TO [DataEntry];
GRANT INSERT ON [Person].[Address] TO [DataEntry];
GRANT UPDATE ON [Person].[Address] TO [DataEntry];
GRANT EXECUTE ON [HumanResources].[uspUpdateEmployeeHireInfo] TO [DataEntry];
DENY VIEW DEFINITION ON [HumanResources].[uspUpdateEmployeeHireInfo] TO [DataEntry];
GRANT EXECUTE ON [HumanResources].[uspUpdateEmployeePersonalInfo] TO [DataEntry]
DENY VIEW DEFINITION ON [HumanResources].[uspUpdateEmployeePersonalInfo] TO [DataEntry];
GO
代码4.2 语句创建角色并分配权限
检查和测试权限
如果你想检查DataEntry角色的权限,你可以使用GUI工具或执行T-SQL代码访问数据库对象的元数据。使用GUI,对象资源管理器->数据库->AdventureWorks2012->安全性->角色->数据库角色->DataEntry->属性。这将打开和创建角色相同的"数据库角色属性"对话框,你可以使用安全对象审查该角色的权限。
或者你可以使用代码4.3中的T-SQL查看DataEntry角色的权限,利用sys.database_permissions和sys.database_principals安全目录视图和sys.objects目录视图。
SELECT DB_NAME() AS 'Database', p.name, p.type_desc, dbp.state_desc,
dbp.permission_name, so.name, so.type_desc
FROM sys.database_permissions dbp
LEFT JOIN sys.objects so ON dbp.major_id = so.object_id
LEFT JOIN sys.database_principals p ON dbp.grantee_principal_id = p.principal_id
WHERE p.name = 'DataEntry'
ORDER BY so.name, dbp.permission_name;
代码4.3 查看DataEntry角色的权限
当你执行上面代码,你将会看到结果如图4.9所示
图4.9 DataEntry角色的权限
你可以通过打开新建查询->更改连接用Topaz登录测试这些权限。然后尝试在HumanResources.Employee、HumanResources.JobCandidate、Person.Address表插入新行或更新已有数据,并执行分配权限的存储过程。这些操作应该成功。然后尝试插入或更新其他表中的数据,这些操作应该失败。你应该只能够执行已经授予的权限。
你可以用代码4.4执行相同的测试。代码开始设置的执行上下文为Topaz,DataEntry角色的成员。然后插入新行到Person.Address表。这一操作成功,因为角色允许在Person.Address表插入数据。然后代码试图插入新行到HumanResources.Department表,插入失败,因为角色在此表没有insert权限。接下来的代码成功执行HumanResources.uspUpdateEmployeePersonalInfo存储过程;执行dbo.uspGetManagerEmployees失败,因为角色没有此过程执行权限。最后,代码将执行上下文切回到登录时的安全上下文。
EXECUTE AS USER = 'Topaz';
-- Succeeds
INSERT INTO Person.Address
(AddressLine1, City, StateProvinceID, PostalCode)
VALUES
('8 Hazelnut', 'Irvine', 9, '');
GO
-- Fails
INSERT INTO HumanResources.Department
(Name, GroupName)
VALUES
('Advertising', 'Sales and Marketing');
GO
-- Succeeds (doesn't actually change any data)
DECLARE @RC INT;
EXECUTE @RC = HumanResources.uspUpdateEmployeePersonalInfo
1, '', '1963-03-02', 'S', 'M';
GO
-- Fails
EXECUTE dbo.uspGetManagerEmployees 1;
GO
REVERT;
代码4.4 测试DataEntry角色的权限
总结
The ability to assign granular permissions on securable objects throughout SQL Server to various types of principals gives you very fine control over the security of a SQL Server instance.它让你充分利用最小特权的安全原则,能够限制主体执行操作和访问数据的能力到他们需要的最低限度。