在進行權限管理時,應遵守“最低權限”原則,即每個人只授予必需的最小權限。相對於授予的權限,數據庫中還有一個特殊的權限,那就是所有權(Ownship)。
SQL Server 用於管理權限的TSQL命令有:GRANT用於授予權限,REVOKE 用於移除已經被GRANT/DENY的權限,而DENY用於防止安全主體通過GRANT獲得權限。DENY一旦執行,Principal在Securable上的權限就被禁用了。但是,SQL Server的權限空間不是扁平的,是立體的,在不同的安全上下文(Security context)中,不同的權限空間(Permission Space)中,這三個命令的優先級是不同的。這就意味着,即使執行GRANT授予權限,用戶不一定有權限,這是因為在特定的權限空間里,Deny命令禁用了用戶的權限,同時Grant命令的優先級低於Deny。
一,管理權限的規則
在管理權限時,要注意權限的上下文、權限的立體空間和權限的優先級
1,安全上下文和權限空間
安全上下文(Security Context),是跟user 或 login 相關的環境,用戶可以通過EXECUTE AS 來切換安全上下文。安全上下文主要包括:Login、User、Role membership、Windows Group membership
權限空間,是指安全對象(Securable)和包含安全對象的所有安全對象類(Securable class),比如,表包含在schema 中,schema是表的安全對象類;而database包含schema,database是schema的安全對象類。訪問表的權限,受到表、schema和database的權限的影響,這三個對象構成一個權限空間,訪問受到權限空間的約束。
2,權限的優先級
當這三個命令作用於同一個安全對象(Securable)時,情況會變得復雜,不僅需要考慮權限空間,還需要考慮權限的優先級。
- 在同一個安全主體范圍內對同一個安全對象設置權限,GRANT子句會移除DENY和REVOKE子句設置的權限,這三個命令的優先級相同,后執行的語句會移除先執行語句的效果。
- 但是,當相同的權限作用於同一安全主體的不同范圍時,如果DENY 作用於更高的范圍內,那么DENY優先,但是在更高的范圍內,REVOKE不優先。
- 這里有一個例外,列級別的GRANT語句,會覆蓋Object級別的DENY語句,但是后續Object級別的DENY語句會覆蓋列級別的GRANT語句。
權限是累積的,一個User可以通過多種途徑(比如Grant、Role和Group memberhsip)來獲得授權,Revoke只能回收某一個途徑上的權限,但是Deny會禁止用戶獲得授權。
舉個例子,一個User通過Grant獲得表1的SELECT權限,通過Role獲得表1的SELECT權限:
- 情況1:當使用REVOKE命令回收GRANT授予的SELECT 權限時,該User仍然可以通過Role的權限來查詢表1。
- 情況2:當使用DENY命令拒絕表1的權限時,該User沒有權限查詢表1。
3,權限的層次結構
權限的層次結構是一種父子結構,擁有父級別對象的權限,默認擁有所有子級別對象的權限。舉個例子,如果有數據庫級別的SELECT權限,那么就有了數據庫下所有Schema的SELECT權限;如果有Schema的SELECT權限,那么就有了Schema下所有對象的SELECT權限,這種權限的結構構成權限空間。
權限是一個覆蓋式的權限,舉個例子,Control表示所有的權限,當對一個對象授予Control權限,意味着授予所有其他的權限。
數據庫級別的權限:
- 在數據庫級別授予操作數據庫對象的權限,比如 EXECUTE、DELETE、INSERT、SELECT、UPDATE、REFERENCES、VIEW DEFINITION,實際上,授予的是操作數據庫中所有對象的權限。
- 數據庫級別獨有的權限:ALTER、BACKUP DATABASE、BACKUP LOG、CHECKPOINT、CONNECT、CREATE TABLE、CREATE VIEW、CREATE PROCEDURE等
二,權限管理的實現
權限管理涉及到Principal、Securable和Permission三個概念。Principal可以是單個User,也可以是多個User構成的Windows Group;Securable可以是單個數據庫對象,也可以是包含多個數據庫對象的schema;同時,User也可以通過role獲得數據庫對象的權限。
第一種方式,為每一個User設置單個Securable的權限
第二種方式,創建Windows Group,把User分組,為每一個Windows Group設置單個Securable的權限,簡少了授權用戶的數量。
第三種方式,通過數據庫 role來設置權限,通過Role來組織Securable,減少了授權對象的數量。
第四種方式,通過Schema來設置權限,在一個Schema下包含多個Securables,並通過Role來管理Scurable,通過Windows Group來管理User,這種方式雖然復雜,但是權限的管理非常精細和靈活。
三,所有權和所有權鏈
對象的所有者對一個對象擁有所有可能的權限,並且這些權限不能禁止。CONTROL權限可以執行與對象所有者幾乎相同的操作,但是所有權和授權是不同的。對象的所有者通常是其創建者,但是可以在創建時使用AUTHORIZATION子句指定其他所有者,也可以把Ownship轉移給其他Principal。
如何在不授予基礎表訪問權限的情況下,僅對視圖或任何其他類型的程序授予SELECT權限呢?答案是使用所有權鏈(Ownership-chaining)。通常情況下,當用戶從視圖中查詢數據時,系統做兩次權限檢查,第一次是檢查用戶是否有權限查詢視圖,第二次是在視圖引用基礎表時檢查用戶是否有權限查詢基礎表,由於用戶沒有基礎表的權限,因此第二次權限檢查失敗。
所有權鏈(Ownership-chaining)通過繞過第二次權限檢查來避免這種情況,否則將在視圖引用基礎表時進行第二次權限檢查。當鏈接的對象(underlying table)與調用對象(view)具有相同的所有者時,權限檢查將被完全繞開。
如果一個user在具有Ownership權限的Schema中創建視圖,因為它視圖的所有者,就是被視圖引用的基表的所有者,這是一個鏈:我是Schema的所有者,那么Schema下的所有對象的Owner都是我,我有權限訪問視圖,但不能訪問基礎表。
所有權鏈只適用於SELECT, INSERT, DELETE, UPDATE 和MERGE,以及 SP和函數的EXECUTE 權限,出於安全考慮,不適用於使用動態SQL的程序中。要在SP、函數和觸發器中執行動態SQL操作,需要使用使用WITH EXECUTE AS子句使用權限模擬。只要調用者對動態SQL中引用的數據庫對象沒有權限,就可以使用此EXECUTE AS子句,但要格外小心。它通過將執行上下文切換到模擬用戶來實現。所有代碼,甚至是嵌套模塊,都將在模擬用戶的安全上下文中執行。當前正在執行的批處理在執行代碼時會臨時獲得例程所有者的權限,而不是所有者的身份。這樣,用戶只能通過更改正在執行的代碼來將特權用於任何其他目的。僅在過程執行完成后或在REVERT語句上,執行上下文才還原為原始調用方。
所有權鏈的問題在於,除了第一次權限檢查之外,所有權鏈接會完全繞過權限檢查,甚至都優先於DENY ACCESS。
參考文檔:
Getting Started with Database Engine Permissions