天天看點

SQL Server安全(6/11):執行上下文與代碼簽名(Execution Context and Code Signing)

在保密你的伺服器和資料,防備目前複雜的攻擊,SQL Server有你需要的一切。但在你能有效使用這些安全功能前,你需要了解你面對的威脅和一些基本的安全概念。這篇文章提供了基礎,是以你可以對SQL Server裡的安全功能充分利用,不用在面對特定威脅,不能保護你資料的功能上浪費時間。

SQL Server決定主體是否有需要的許可執行代碼的基本方式是它的執行上下文角色。這都是複雜的可能性,主體有執行代碼的許可,但沒有代碼通路的潛在對象的許可,例如表裡的資料。這篇文章會探尋SQL Server執行上下文,所有權連結接,模拟,還有向你展示下如果通過T-SQL代碼控制資料通路。

執行上下文

當使用者執行一個存儲過程或其它資料庫代碼時,SQL Server檢查確定,不僅使用者有運作存儲過程的許可,而且有代碼通路使用資料庫對象的許可。沒有這類許可檢查,有些人會很容易建立可以讀取表,使用執行代碼通路不需要其它的對象代碼。這會是重大的安全漏洞。

這個許可檢查過程不發生的唯一例外是代碼的所有者也是代碼通路的所有潛在對象的所有者。在這個共同所有權下,SQL Server驗證調用者在代碼上有EXECUTE許可,不會繼續檢查許可。

例如,如果在存儲過程中的嗲嗎通路三個表和四個視圖,SQL Server在執行代碼前進行這些概念上的步驟:

  1. 在代碼上驗證調用者有EXECTUTE許可。如果調用者沒有,傳回一個錯誤并不再繼續。
  2. 檢查代碼的所有者是否也有代碼通路對象的許可。如果共同所有權(common ownership)存在的話,停止檢查并執行代碼。
  3. 如果共同所有權(common ownership)不存在的話,檢查確定調用者代碼通路的對象上有許可。如果調用者在一個或多個對象上沒有許可,傳回一個錯誤且不執行代碼。
  4. 如果調用者有所有需要的許可,執行代碼。否則,傳回錯誤且不執行代碼。

在代碼調用其它帶按摩或通路其它對象的地方,共同所有權繼續檢查,即輪流調用其它代碼或通路其它對象。隻要鍊裡的所有對象有同樣的所有者,許可檢查就不需要。但隻要鍊裡的一個對象比要通路它的對象多出不同的所有者,在那個對象上的許可就檢查了。

在這鐘情形裡的對象所有權被稱為所有權連結接(ownership chain),因為你不需要擔心代碼執行的安全上下文。這也是SQL Server的早期版本有擁有所有對象的特定dbo角色的直接原因。但任何時候你有共同所有權和許可來通路一切,你就違法了最小特權許可,暴露你的資料在不需要的安全危機裡。

幸運的是,在SQL Server裡你可以修改代碼的安全執行上下文。

提示:

這篇文章會探尋執行在存儲過程上的執行上下文和代碼簽名,但它們同樣對大多數使用者自定義函數也支援。

修改執行上下文

一般你不想調用者的許可用來在破壞的所有權連結接裡驗證許可。有時你想代碼好像完全被另一個使用者執行一樣,通過另一個使用者的許可在通路的所有的對象上驗證許可。這稱為切換代碼的執行上下文。這讓你使用SQL Server顆粒度許可的優點,對潛在的對象保持完全的許可控制,但還是給不同使用者執行代碼的能力。

在SQL Server裡,當你定義任何類型的使用者自定義函數(行内表值函數除外),存儲過程和觸發器,你可以使用EXECUTE AS子句作為對象定義的一部分,表示這個代碼應該在指定使用者的安全上下文下運作。

EXECUTE AS有4個可用選項:

  • EXECUTE AS CALLER:預設使用者向下相容。代碼在調用者的上下文裡執行,調用者必須同時有執行代碼和通路潛在對象的許可。實際的操作取決于所有權連結接上是否損壞或完好。
  • EXECUTE AS = ‘username’ and EXECUTE AS = ‘loginname’:代碼在指定使用者或登入的上下文裡運作,是以指定的使用者或登入必須在所有的潛在對象上有許可。在這個情況下,調用者必須滿足下列之一:
    • 在代碼上有EXECUTE許可
    • 是sysadmin或db_owner,或者在伺服器或資料庫上有CONTROL SERVER許可,或者對于使用者有模仿(impersonate)許可。

使用使用者名的EXECUTE AS隻能應用于伺服器範圍的DDL觸發器,且要登入到觸發器。否則,提供的使用者名必須是有效的資料庫使用者名稱。

  • EXECUTE AS SELF:這是建立存儲過程的目前使用者的縮寫。和EXECUTE = [myUserName]等效。SQL Server目錄村裡寫代碼的實際使用者ID。
  • EXECUTE AS OWNER:這是在指定使用者的安全上下文運作的另一個變體,在這個情況下,代碼所有者在代碼執行時間,而不是在建立時間。如果在資料庫裡,擁有者在代碼建立後修改了,這表示代碼會在和首次建立代碼的不同使用者的許可執行。

當你在SSMS裡運作代碼時,在會話的執行上下文裡,有兩種EXECUTE AS的變體可以作為語句使用。它們是EXECUTE AS LOGIN = ‘loginname’ 和EXECUTE AS USER = ‘username’。當使用者登入到SQL Server執行個體時,會話開始,那個時候的執行上下文設定為登入的使用者,用作許可檢查。EXECUTE AS 修改會話期間執行上下文,直到使用者執行了REVERT語句。

通過EXECUTE AS修改安全上下文的任何時間,代碼建立者或會話使用者在語句裡指定的使用者必須有模仿(impersonate)許可。你永遠不需要模拟自己的許可,例如EXECUTE AS SELF。

使用EXECUTE AS子句

在資料庫裡,假設你有Vendor表。表在SchemaUserTable架構裡定義,屬于UserTable使用者。代碼6.1定義了範文這個表的存儲過程。在SchemaUserProc定義的存儲過程,屬于UserProc使用者。因為表和存儲過程在屬于不同使用者的不同架構裡定義,存在斷開的所有權連結接。

1 USE ExecuteContextDB;
2 GO
3 CREATE PROC SchemaUserProc.VendorAccessProc @state CHAR(2)
4 AS 
5     SELECT * FROM SchemaUserTable.Vendor WHERE state = @state;
6 GO      

代碼6.1:建立在一個在一個架構裡通路不同架構裡的表的存儲過程,這裡架構有不同的擁有者。

下面的代碼會建立登入、資料庫、使用者和這個部分使用的架構,同樣也會在Vendor表裡插入一些記錄。先運作下列帶代碼再運作代碼6.1。

1 -- Create the logins and database for this demo
 2 USE master;
 3 GO
 4 
 5 IF SUSER_SID('UserProc') IS NOT NULL DROP LOGIN UserProc;
 6 IF SUSER_SID('UserTable') IS NOT NULL DROP LOGIN UserTable;
 7 IF SUSER_SID('RealUser') IS NOT NULL DROP LOGIN RealUser;
 8 GO
 9 CREATE LOGIN UserProc WITH password = 'Y&2!@37z#F!l1zB';
10 CREATE LOGIN UserTable WITH password = 'Y&2!@37z#F!l1zB';
11 CREATE LOGIN RealUser WITH password = 'Y&2!@37z#F!l1zB';
12 GO
13 
14 IF DB_ID('ExecuteContextDB') IS NOT NULL DROP DATABASE ExecuteContextDB;
15 CREATE DATABASE ExecuteContextDB;
16 GO
17 USE ExecuteContextDB;
18 GO
19 
20 -- Create the users 
21 CREATE USER UserProc;
22 CREATE USER UserTable;
23 CREATE USER RealUser;
24 GO
25 
26 -- Create the schemas 
27 CREATE SCHEMA SchemaUserProc AUTHORIZATION UserProc;
28 GO
29 CREATE SCHEMA SchemaUserTable AUTHORIZATION UserTable;
30 GO
31 
32 -- Create a table and a proc in different schemas to ensure that 
33 -- there is no ownerhship chaining.
34 CREATE TABLE SchemaUserTable.Vendor 
35     (ID INT, name VARCHAR(50), state CHAR(2), phno CHAR(12));
36 GO
37 SET NOCOUNT ON
38 GO
39 INSERT INTO SchemaUserTable.Vendor VALUES (1,'Vendor1','AK','123-345-1232');
40 INSERT INTO SchemaUserTable.Vendor VALUES (2,'Vendor2','WA','454-765-3233');
41 INSERT INTO SchemaUserTable.Vendor VALUES (3,'Vendor3','OR','345-776-3433');
42 INSERT INTO SchemaUserTable.Vendor VALUES (4,'Vendor4','AK','232-454-5654');
43 INSERT INTO SchemaUserTable.Vendor VALUES (5,'Vendor5','OR','454-545-5654');
44 INSERT INTO SchemaUserTable.Vendor VALUES (6,'Vendor6','HI','232-655-1232');
45 INSERT INTO SchemaUserTable.Vendor VALUES (7,'Vendor7','HI','453-454-1232');
46 INSERT INTO SchemaUserTable.Vendor VALUES (8,'Vendor8','WA','555-654-1232');
47 INSERT INTO SchemaUserTable.Vendor VALUES (9,'Vendor9','AK','555-345-1232');
48 GO      

代碼6.2在存儲過程上授予EXECUTE許可給真正的使用者,RealUser,它會運作代碼。

1 -- Grant permissions on the stored procedure
2 GRANT EXECUTE ON SchemaUserProc.VendorAccessProc TO RealUser;
3 GO      

代碼6.2:在新的存儲過程上授予EXECUTE許可。

在SSMS裡,你可以運作EXECUTE AS作為臨時修改安全上下文的在查詢窗體裡運作的代碼語句。使用代碼6.3修改安全上下文為RealUser來運作存儲過程獲得在位于阿拉斯加的供應商清單。

1 EXECUTE AS user = 'RealUser';
2 EXEC SchemaUserProc.VendorAccessProc 'AK';      

代碼6.3:修改執行上下文,以RealUser運作存儲過程。

執行這個代碼引起了下列錯誤:

SQL Server安全(6/11):執行上下文與代碼簽名(Execution Context and Code Signing)

在對象Vendor上,SELECT許可被拒絕,資料庫 'ExecuteContextDB',架構 'SchemaUserTable'。

問題是所有權連結接斷開了——存儲過程的所有者和表的所有者不同——RealUser在Vendor表上沒有SELECT許可。這裡有SQL Server如何從概念上分析情況:

  • 調用者是RealUser,它有EXECUTE許可。通過!
  • 存儲過程的所有者是UserProc。表的所有者是UserTable。這表示有斷開的所有權連結接,是以檢查調用者RealUser,在代碼裡有進行操作的許可。
  • RealUser在Vendor表上沒有SELECT許可,抛出錯誤。失敗!

你可以在存儲過程定義裡使用EXECUTE AS子句來修正問題,假設你是存儲過程的建立者,在這個情況裡想允許RealUser運作代碼。首先,使用代碼6.1顯示的REVERT語句來撤銷安全上下文的RealUser切換,傳回你自己的安全上下文:

1 REVERT;      

代碼6.4:恢複使用者運作SSMS的原始安全上下文

接下來,修改存儲過程來包含EXECUTE AS子句使用UserTable的安全上下文來運作存儲過程,它在表上擁有SELECT許可,如代碼6.5所示。

1 ALTER PROC SchemaUserProc.VendorAccessProc @state CHAR(2)
2 WITH EXECUTE AS 'UserTable'
3 AS 
4     SELECT * FROM SchemaUserTable.Vendor WHERE state = @state;
5 GO      

代碼6.5:修改存儲過程,在運作時間使用EXECUTE AS修改運作上下文

在這個例子裡,UserTable通過成員資格擁有在Vendor表上的SELECT許可。但成員資格沒有必要用來執行上下文切換工作。例如,可以通過表擁有者授予使用者EXECUTE AS user許可。

然後修改安全上下文為UserTable,再次嘗試運作存儲過程,使用代碼6.6。

1 EXECUTE AS user = 'RealUser';
2 EXEC SchemaUserProc.VendorAccessProc 'AK';
3 REVERT;      

代碼6.6:測試修改後的存儲過程來看看RealUser現在能否執行代碼。

這次調用成功,因為當SQL Server在所有權連結接檢查許可時——還是斷開的——它發現UserTable有需要的SELECT許可。結果如插圖6.1所示。

SQL Server安全(6/11):執行上下文與代碼簽名(Execution Context and Code Signing)

插圖6.1:在不同的使用者安全上下文下運作存儲過程的結果

代碼簽名

使用EXECUTE AS子句修改T-SQL代碼段的安全上下文隻是解決斷開所有權連結接問題的一種方法。另一個方法是使用證書或非對稱匙的簽名代碼。這個技術授予代碼本身許可,不需要你修改運作上下文或取決于調用者的許可。而且通過證書的使用或非對稱匙的嚴格控制,你還是可以控制哪個主體能利用運作代碼的許可。

這個方式的方法是你建立一個安全,加密的證書或非對稱匙,然後建立與證書或匙關聯的使用者。這是特别的使用者類型,不關聯登入。你配置設定需要的許可來運作到使用者的存儲過程,然後使用ADD SIGNATURE語句配置設定證書或匙到存儲過程。存儲過程使用證書或匙關聯的使用者許可。

即使存儲過程使用EXECUTE AS語句修改執行上下文,你還是可以使用這個技術。代碼簽名的常見情形是,修改執行上下文到代碼需要執行的最多許可的運作上下文,然後使用代碼簽名添加一個或多個額外許可。

和往常一樣,需要通過執行個體來示範下這個技術。代碼6.7建立再次從ExecuteContextDB資料庫的Vendor表擷取資料。UnsignedProc存儲過程沒有簽名,是以當RealUser運作它的時候會失敗。SignedProc存儲過程會簽名,對于RealUser使用者會正常運作。

1 CREATE PROC SchemaUserProc.UnsignedProc @state CHAR(2)
 2 AS
 3     SELECT * FROM SchemaUserTable.Vendor WHERE state = @state;
 4 GO
 5 CREATE PROC SchemaUserProc.SignedProc @state CHAR(2)
 6 AS
 7     SELECT * FROM SchemaUserTable.Vendor WHERE state = @state;
 8 GO
 9 
10 GRANT EXECUTE ON SchemaUserProc.UnsignedProc TO RealUser;
11 GRANT EXECUTE ON SchemaUserProc.SignedProc TO RealUser;
12 GO      

代碼6.7:建立唯一的存儲過程并對RealUser在它們上面授予EXECUTE許可

但這次,我們不是通過修改運作上下文授予SELECT許可,我們會建立一個證書,如代碼6.8所示。代碼然後建立從證書建立一個使用者,對使用者在Vendor表上授予SELECT許可。最後,代碼使用ADD SIGNATURE語句添加證書到SignedProc存儲過程。注意隻有SignedProc拿到簽名;UnsignedProc還是沒簽名。

1 CREATE CERTIFICATE MyCertificate
 2     ENCRYPTION BY PASSWORD = 'SZ6T4O^ff&1Kr3s?m\*'
 3     WITH SUBJECT = 'Certificate to sign SignedProc';
 4 GO
 5 
 6 CREATE USER MyCertificateUser 
 7     FROM CERTIFICATE MyCertificate;
 8 
 9 GRANT SELECT ON SchemaUserTable.Vendor TO MyCertificateUser;
10 GO
11 
12 ADD SIGNATURE TO SchemaUserProc.SignedProc BY CERTIFICATE MyCertificate
13     WITH PASSWORD = 'SZ6T4O^ff&1Kr3s?m\*';
14 GO      

代碼6.8:實作證書和配置設定許可到代碼的代碼

最後,到測試簽名架構代碼的時候了,如代碼6.9所示。結果如插圖6.2所示,UnsignedProc有斷開的所有權連結接,RealUser在Vendor表上沒有SELECT許可,是以執行失敗。SignedProc通過使用代碼簽名授予SELECT許可,成功執行傳回三條阿拉斯加的資料。

1 EXECUTE AS USER = 'RealUser';
2 
3 -- Can't run UnsignedProc
4 EXEC SchemaUserProc.UnsignedProc 'AK';
5 -- Can run SignedProc
6 EXEC SchemaUserProc.SignedProc 'AK';
7 
8 REVERT;      

代碼6.9:測試使用證書簽名的代碼

SQL Server安全(6/11):執行上下文與代碼簽名(Execution Context and Code Signing)

插圖6.2:測試在UnsignedProc和SignedProc存儲過程代碼簽名的結果

配置這個有點複雜,但安全上受益非常值得。使用正确的話,這個技術剔除了使用者在潛在對象上需要的SELECT許可和存儲過程上的EXECUTE許可。很可能這不是你在存儲過程或使用者自定義函數裡廣泛使用的,但當處理斷開的所有權連結接且沒有友善的擁有需要許可的主體時,它漂亮的解決了一些安全問題。

小結

在SQL Server裡建立存儲過程和使用者自定義函數的最簡單方法是用完整的所有權連結接來是實作它們,代碼的所有者也擁有代碼通路的所有資料庫對象。但通常這并不可行,當在SQL Server執行個體裡對象的成員資格在多個主體間分發的時候。這篇檔案介紹了你可以處理斷開的所有權連結接的兩個技術,通過修改執行上下文和使用代碼簽名配置設定許可。這些技術是免費贈送的,是以你可以對單個存儲過程或函數一起使用它們。那樣的話,你可以處理你面對的任何許可架構,來保持你的資料庫及它的資料庫盡可能的安全。

原文連結

http://www.sqlservercentral.com/articles/Stairway+Series/121476/

注:此文章為

WoodyTu

學習MS SQL技術,收集整理相關文檔撰寫,歡迎轉載,請在文章頁面明顯位置給出此文連結!

若您覺得這篇文章還不錯請點選下右下角的推薦,有了您的支援才能激發作者更大的寫作熱情,非常感謝!

繼續閱讀