在SQL Server數據庫中,登錄賬號分類如下:
(1) SQL賬號,需要單獨設置密碼,比如:sa;
(2) Windows賬號,登錄windows的賬號,比如: administrator,不需要另設密碼;
(3) Windows Group 賬號, 為本地用戶組或域用戶組,將組添加到登錄,組成員不需要單獨創建登錄;
查看Windows賬號,是否屬於某一個/多個用戶組:
exec xp_logininfo 'windows_acount','ALL' --域用戶格式為:domain_name\account_name
以下腳本,均假設最終登錄賬號為:test_login,所有數據庫對應的user為test_user
一. 有沒有權限
1. 檢查有沒有登錄權限
--是否存在有效的登錄賬號:是否被禁用,sql login還有:密碼是否過期,是否被鎖定 select is_disabled, loginproperty(name,'Isexpired') is_expired, loginproperty(name,'Islocked') is_locked, * from sys.server_principals where name = 'test_login'
2. 檢查有沒有訪問某數據庫的權限
USE DBA GO --檢查是否有數據庫的CONNECT權限即可 select b.* from sys.database_principals a inner join sys.database_permissions b on a.principal_id = b.grantee_principal_id where SUSER_SNAME(a.sid) = 'test_login' and b.permission_name = 'CONNECT' --老的系統表sysusers也可以檢查 SELECT name, hasdbaccess,* FROM sysusers a WHERE SUSER_SNAME(a.sid) = 'test_login'
如果有很多個數據庫,寫個游標1個個去檢查即可。
3. 檢查有沒有某個對象的權限
檢查有沒有某個對象的權限,一般是去嘗試運行下腳本比較直觀,如果去查各種權限表,角色錯綜復雜時,很難分辨;
SQL Server 2008之后引入了HAS_PERMS_BY_NAME這個函數,它可以檢查當前賬號的各種權限,檢查其他用戶需要用EXECUTE AS來切換:
USE DBA GO EXECUTE AS user = 'test_user' GO --對象權限 SELECT HAS_PERMS_BY_NAME('Sales.SalesPerson', 'OBJECT', 'INSERT'); SELECT HAS_PERMS_BY_NAME('sp_send_dbmail', 'OBJECT', 'EXEC'); --架構權限 SELECT HAS_PERMS_BY_NAME('test_schema', 'SCHEMA', 'SELECT'); REVERT; GO
對於是否有登錄、訪問數據庫的權限,用這個函數也可以判斷:
USE master GO EXECUTE AS login = 'test_login' GO --登錄權限,本機前2個參數為空即可 SELECT HAS_PERMS_BY_NAME(NULL, NULL, 'CONNECT SQL'); REVERT; GO USE DBA GO EXECUTE AS user = 'test_user' GO --數據庫權限 SELECT HAS_PERMS_BY_NAME(db_name(), 'DATABASE', 'CONNECT'); REVERT;
二. 有哪些權限
權限可以直接分配給賬號,也可以分配給賬號所屬的role,所以要把賬號自身權限、所屬role權限合並才是最終的賬號權限。
Windows賬號權限還可以通過用戶組分配,所以還要檢查這個Windows賬號有沒有屬於某個用戶組,如果有還需要加上這個用戶組的權限;
下面的腳本,僅檢查單個用戶/用戶組權限。
1. 實例級的權限
use master GO declare @svr_principal_name varchar(1024) set @svr_principal_name = 'test_login' declare @svr_principal_id int select @svr_principal_id = principal_id from sys.server_principals p where p.name = @svr_principal_name if OBJECT_ID('tempdb..#tmp_svr_role','U') is not null drop table #tmp_svr_role; create table #tmp_svr_role ( member_principal_id int, member_principal_name varchar(512), role_principal_id int, role_principal_name varchar(512) ) --獲取登錄賬號的所有server role, 從sql server 2012開始,server role可以自定義,成員僅可為fixed server role ;with tmp as ( select * from sys.server_role_members where member_principal_id = @svr_principal_id union all select rm.* from sys.server_role_members rm inner join tmp on rm.member_principal_id = tmp.role_principal_id ) insert into #tmp_svr_role select a.member_principal_id, b.name, a.role_principal_id, c.name from tmp a inner join sys.server_principals b on a.member_principal_id = b.principal_id inner join sys.server_principals c on a.role_principal_id = c.principal_id --登錄賬號自身權限, sys.server_permissions不包含fixed server role權限,同時手動排除掉public權限 select a.principal_id as member_principal_id, a.name as member_principal_name, null as role_principal_id, null as role_principal_name, b.permission_name, b.state_desc from sys.server_principals a inner join sys.server_permissions b on a.principal_id = b.grantee_principal_id where a.principal_id = @svr_principal_id and b.permission_name <> 'CONNECT SQL' union all --server role權限,包含fixed server role和自定義的server role select a.member_principal_id, a.member_principal_name, a.role_principal_id, a.role_principal_name, isnull(b.permission_name,'Fixed Server-Level Role: '+role_principal_name) as permission_name, isnull(b.state_desc,'GRANT') as state_desc from #tmp_svr_role a left join sys.database_permissions b on a.role_principal_id = b.grantee_principal_id union all --public server role權限,不可以取消public權限,它是每個登錄賬號的最小權限,僅可連接數據庫實例 select @svr_principal_id as member_principal_id,@svr_principal_name as member_principal_name, principal_id as role_principal_id, name as role_principal_name, 'CONNECT SQL' as permission_name, 'GRANT' as state_desc from sys.server_principals where name = 'public'
注意:服務器角色的權限可以做什么具體的事情,exec sp_srvrolepermission 有大致的介紹,但是也並沒有全部列出每一種數據庫操作,因為有些操作是被更高級的操作包含的。
2. 數據庫級的權限
僅列出數據庫級別的權限,具體的對象名稱並沒有列出。
use DBA GO declare @svr_principal_name varchar(1024) set @svr_principal_name = 'test_login' declare @db_principal_id int, @db_principal_name varchar(512) select @db_principal_id = principal_id, @db_principal_name = name from sys.database_principals p where SUSER_SNAME(sid) = @svr_principal_name if OBJECT_ID('tempdb..#tmp_db_role','U') is not null drop table #tmp_db_role; create table #tmp_db_role ( member_principal_id int, member_principal_name varchar(512), role_principal_id int, role_principal_name varchar(512) ) --獲取登錄賬號在當前數據庫的所有database role ;with tmp as ( select * from sys.database_role_members where member_principal_id = @db_principal_id union all select rm.* from sys.database_role_members rm inner join tmp on rm.member_principal_id = tmp.role_principal_id ) insert into #tmp_db_role select a.member_principal_id, b.name, a.role_principal_id, c.name from tmp a inner join sys.database_principals b on a.member_principal_id = b.principal_id inner join sys.database_principals c on a.role_principal_id = c.principal_id --登錄賬號在當前數據庫的自身權限, sys.database_permissions不包含fixed database role權限,同時手動排除掉public權限 select a.principal_id as member_principal_id, a.name as member_principal_name, null as role_principal_id, null as role_principal_name, b.permission_name, b.state_desc from sys.database_principals a inner join sys.database_permissions b on a.principal_id = b.grantee_principal_id where a.principal_id = @db_principal_id and b.permission_name <> 'CONNECT' union all --database role權限,包含fixed database role和自定義的database role select a.member_principal_id, a.member_principal_name, a.role_principal_id, a.role_principal_name, isnull(b.permission_name,'Fixed Database-Level Role: '+role_principal_name) as permission_name, isnull(b.state_desc,'GRANT') as state_desc from #tmp_db_role a left join sys.database_permissions b on a.role_principal_id = b.grantee_principal_id union all --public database role權限,不可以取消public權限,它是每個登錄賬號映射到當前數據庫的最小權限,僅可連接當前數據庫 select @db_principal_id as member_principal_id, @db_principal_name as member_principal_name, principal_id as role_principal_id, name as role_principal_name, 'CONNECT' as permission_name, 'GRANT' as state_desc from sys.database_principals where name = 'public'
注意:sysadmin的賬號在數據庫里可能並沒有做映射,但權限是有的,隱式映射的用戶是dbo
3. 對象級的權限
sys.database_permissions有很多對象類型,major_id, minor_id取決於class_desc,不同的對象關聯不同的系統表/視圖,腳本里僅列出了最常見的OBJECT_OR_COLUMN, SCHEMA對象權限。
--建立測試用的架構,對象,列 use DBA GO if object_id('test_grant','U') is not null drop table test_grant GO create table test_grant(c1 int, c2 int, c3 int) grant select (c1, c2) on test_grant to test_user; if object_id('test_schema.test_t1','U') is not null drop table test_schema.test_t1 GO if exists(select 1 from sys.schemas where name = 'test_schema') drop schema test_schema GO create schema test_schema create table test_schema.test_t1(c1 int, c2 int) grant select on schema::test_schema to test_user; GO --開始獲取對象權限 use DBA GO declare @svr_principal_name varchar(1024) set @svr_principal_name = 'test_login' declare @db_principal_id int, @db_principal_name varchar(512) select @db_principal_id = principal_id, @db_principal_name = name from sys.database_principals p where SUSER_SNAME(sid) = @svr_principal_name if OBJECT_ID('tempdb..#tmp_db_role','U') is not null drop table #tmp_db_role; create table #tmp_db_role ( member_principal_id int, member_principal_name varchar(512), role_principal_id int, role_principal_name varchar(512) ) --獲取登錄賬號在當前數據庫的所有database role ;with tmp as ( select * from sys.database_role_members where member_principal_id = @db_principal_id union all select rm.* from sys.database_role_members rm inner join tmp on rm.member_principal_id = tmp.role_principal_id ) insert into #tmp_db_role select a.member_principal_id, b.name, a.role_principal_id, c.name from tmp a inner join sys.database_principals b on a.member_principal_id = b.principal_id inner join sys.database_principals c on a.role_principal_id = c.principal_id --登錄賬號在當前數據庫的自身對象權限(OBJECT_OR_COLUMN) select a.principal_id as member_principal_id, a.name as member_principal_name, null as role_principal_id, null as role_principal_name, o.name as major_name, c.name as minor_name, b.permission_name, b.state_desc from sys.database_principals a inner join sys.database_permissions b on a.principal_id = b.grantee_principal_id left join sys.objects o on b.major_id = o.object_id left join sys.columns c on (b.major_id = c.object_id and b.minor_id = c.column_id) where a.principal_id = @db_principal_id and b.class_desc = 'OBJECT_OR_COLUMN' union all --登錄賬號在當前數據庫的自身對象權限(SCHEMA) select a.principal_id as member_principal_id, a.name as member_principal_name, null as role_principal_id, null as role_principal_name, s.name as major_name, null as minor_name, b.permission_name, b.state_desc from sys.database_principals a inner join sys.database_permissions b on a.principal_id = b.grantee_principal_id left join sys.schemas s on b.major_id = s.schema_id where a.principal_id = @db_principal_id and b.class_desc = 'SCHEMA' union all --database role的對象權限(OBJECT_OR_COLUMN) select a.member_principal_id, a.member_principal_name, a.role_principal_id, a.role_principal_name, o.name as major_name, c.name as minor_name, b.permission_name, b.state_desc from #tmp_db_role a inner join sys.database_permissions b --inner join, 僅自定義的database role on a.role_principal_id = b.grantee_principal_id left join sys.objects o on b.major_id = o.object_id left join sys.columns c on (b.major_id = c.object_id and b.minor_id = c.column_id) where b.class_desc = 'OBJECT_OR_COLUMN' union all --database role的對象權限(SCHEMA) select a.member_principal_id, a.member_principal_name, a.role_principal_id, a.role_principal_name, s.name as major_name, null as minor_name, b.permission_name, b.state_desc from #tmp_db_role a inner join sys.database_permissions b --inner join, 僅自定義的database role on a.role_principal_id = b.grantee_principal_id left join sys.schemas s on b.major_id = s.schema_id where b.class_desc = 'SCHEMA' /* union all --public role有一些系統視圖的select權限,可以忽略 select a.principal_id as member_principal_id, a.name as member_principal_name, null as role_principal_id, null as role_principal_name, o.name as major_name, c.name as minor_name, b.permission_name, b.state_desc from sys.database_principals a inner join sys.database_permissions b on a.principal_id = b.grantee_principal_id left join sys.all_objects o on b.major_id = o.object_id left join sys.all_columns c on (b.major_id = c.object_id and b.minor_id = c.column_id) where a.name = 'public' */
注意:如果對象的權限是通過role衍生的,而不是直接分配給user或者role,那么並不會被列出來。試想sysadmin 的角色,難道要列出所有數據庫的所有對象嗎?
三. 查看自己的權限
1. 有沒有登錄權限
登錄失敗並不一定是沒權限,還是找別人來檢查自己賬號的登錄權限吧;
2. 有沒有數據庫訪問權限
--列出所有可訪問的數據庫 SELECT * FROM sys.databases WHERE HAS_DBACCESS(name) = 1
3. 有沒有對象訪問權限
用上面提到HAS_PERMS_BY_NAME函數,它可以檢查當前賬號的各種權限;
SELECT HAS_PERMS_BY_NAME('test_sp', 'Object' , 'Execute') SELECT HAS_PERMS_BY_NAME('test', 'Database' , 'Execute')
4. 有哪些權限
--實例級權限 SELECT * FROM fn_my_permissions(NULL, 'SERVER'); --數據庫級權限 SELECT * FROM fn_my_permissions ('DBA', 'DATABASE'); --對象權限,只能一個個對象檢查,不能一次返回所有對象權限,和HAS_PERMS_BY_NAME類似 SELECT * FROM fn_my_permissions ('test_grant', 'OBJECT');
用於檢查自己權限的方法,同樣也可以檢查其他賬號,用EXECUTE AS切換賬號即可。
