SQL Server 2016:行級安全
對于SQL Server,一個常見的批評是,其安全模型只能識別表和列。用戶如果希望以行為單位應用安全規則,就需要使用存儲過程或表值函數來模擬,然后找一種方法,確保它們不會被繞開。在SQL Server 2016中,那不再是個問題。
實現
SQL Server 2016(及SQL Azure)中的 行級安全 基于一個專門設計的內聯表值函數。該函數要么返回一個只包含值1的行,要么不返回結果,這取決于用戶訪問的行是否是相關行。請看下面的函數:
CREATE FUNCTION Security.fn_securitypredicate(@SalesRep AS sysname) RETURNS TABLE WITH SCHEMABINDING AS RETURN SELECT 1 AS fn_securitypredicate_result WHERE @SalesRep = USER_NAME() OR USER_NAME() = 'Manager';
這段代碼的意思是,當前用戶必須是一名經理,或者是一名與記錄相關的推銷員。該函數沒有訪問行本身,但用戶可以使用參數傳入相應的列(比如,SalesRep)。例如:
CREATE SECURITY POLICY SalesFilter ADD FILTER PREDICATE Security.fn_securitypredicate(SalesRep) ON dbo.Sales WITH (STATE = ON);
實際效果
在使用行級安全時,用戶無法看到他們不能訪問的行。這就好像在訪問表時自動增加一個額外的、安全相關的where子句。
由于其作用像一個where子句,所以有一些局限。例如,如果用戶在那個列上使用了全文搜索索引,那么數據就可能泄露。此外,數據庫還可能遭受旁路攻擊。微軟寫道:
通過使用精心設計的查詢,可以導致信息泄露。例如,SELECT 1/(SALARY-100000) FROM PAYROLL WHERE NAME='John Doe' 會讓一個惡意用戶知道John Doe的工資是10萬美元。即使有一個恰當的安全謂詞阻止惡意用戶直接查詢其它人的工資,他也可以在查詢返回“除數為0”的異常時確定工資數額。
此外,信息也可能通過統計對象泄露。為了降低風險,查看受保護列統計信息的用戶必須是“表的所有者,或者是服務器固有角色sysadmin、數據庫固有角色db_owner或db_ddladmin的成員”。
中間層應用程序
截至目前,我們討論的場景是用戶以自己的身份登錄。在中間層應用程序中,所有人都共享同一個數據庫賬戶,實現行級安全需要額外的步驟。
對于中間層應用程序,推薦的設計模式是將 CONTEXT_INFO 的值設置為連接打開時用戶特定于應用程序的用戶id。然后,安全函數就可以引用CONTEXT_INFO的值。例如:
CREATE FUNCTION Security.fn_securitypredicate(@AppUserId int) RETURNS TABLE WITH SCHEMABINDING AS RETURN SELECT 1 AS fn_securitypredicate_result WHERE DATABASE_PRINCIPAL_ID() = DATABASE_PRINCIPAL_ID('dbo') -- 應用程序上下文 AND CONVERT(int, CONVERT(VARBINARY(4), CONTEXT_INFO())) = @AppUserId; -- AppUserId (int)占4個字節 GO CREATE SECURITY POLICY Security.SalesFilter ADD FILTER PREDICATE Security.fn_securitypredicate(AppUserId) ON dbo.Sales WITH (STATE = ON);
該方法的前提是,用戶無法執行任意SQL,因為那會讓他們可以隨意更改CONTEXT_INFO。
查看英文原文: SQL Server 2016: Row-Level Security