SQL Server安全(6/11):执行上下文与代码签名(Execution Context and Code Signing)
在保密你的服務(wù)器和數(shù)據(jù),防備當(dāng)前復(fù)雜的攻擊,SQL Server有你需要的一切。但在你能有效使用這些安全功能前,你需要理解你面對(duì)的威脅和一些基本的安全概念。這篇文章提供了基礎(chǔ),因此你可以對(duì)SQL Server里的安全功能充分利用,不用在面對(duì)特定威脅,不能保護(hù)你數(shù)據(jù)的功能上浪費(fèi)時(shí)間。
SQL Server決定主體是否有需要的許可執(zhí)行代碼的基本方式是它的執(zhí)行上下文角色。這都是復(fù)雜的可能性,主體有執(zhí)行代碼的許可,但沒(méi)有代碼訪問(wèn)的潛在對(duì)象的許可,例如表里的數(shù)據(jù)。這篇文章會(huì)探尋SQL Server執(zhí)行上下文,所有權(quán)鏈接接,模擬,還有向你展示下如果通過(guò)T-SQL代碼控制數(shù)據(jù)訪問(wèn)。
執(zhí)行上下文
當(dāng)用戶(hù)執(zhí)行一個(gè)存儲(chǔ)過(guò)程或其它數(shù)據(jù)庫(kù)代碼時(shí),SQL Server檢查確保,不僅用戶(hù)有運(yùn)行存儲(chǔ)過(guò)程的許可,而且有代碼訪問(wèn)使用數(shù)據(jù)庫(kù)對(duì)象的許可。沒(méi)有這類(lèi)許可檢查,有些人會(huì)很容易創(chuàng)建可以讀取表,使用執(zhí)行代碼訪問(wèn)不需要其它的對(duì)象代碼。這會(huì)是重大的安全漏洞。
這個(gè)許可檢查過(guò)程不發(fā)生的唯一例外是代碼的所有者也是代碼訪問(wèn)的所有潛在對(duì)象的所有者。在這個(gè)共同所有權(quán)下,SQL Server驗(yàn)證調(diào)用者在代碼上有EXECUTE許可,不會(huì)繼續(xù)檢查許可。
例如,如果在存儲(chǔ)過(guò)程中的嗲嗎訪問(wèn)三個(gè)表和四個(gè)視圖,SQL Server在執(zhí)行代碼前進(jìn)行這些概念上的步驟:
在代碼調(diào)用其它帶按摩或訪問(wèn)其它對(duì)象的地方,共同所有權(quán)繼續(xù)檢查,即輪流調(diào)用其它代碼或訪問(wèn)其它對(duì)象。只要鏈里的所有對(duì)象有同樣的所有者,許可檢查就不需要。但只要鏈里的一個(gè)對(duì)象比要訪問(wèn)它的對(duì)象多出不同的所有者,在那個(gè)對(duì)象上的許可就檢查了。
在這鐘情形里的對(duì)象所有權(quán)被稱(chēng)為所有權(quán)鏈接接(ownership chain),因?yàn)槟悴恍枰獡?dān)心代碼執(zhí)行的安全上下文。這也是SQL Server的早期版本有擁有所有對(duì)象的特定dbo角色的直接原因。但任何時(shí)候你有共同所有權(quán)和許可來(lái)訪問(wèn)一切,你就違法了最小特權(quán)許可,暴露你的數(shù)據(jù)在不需要的安全危機(jī)里。
幸運(yùn)的是,在SQL Server里你可以修改代碼的安全執(zhí)行上下文。
提示:
這篇文章會(huì)探尋執(zhí)行在存儲(chǔ)過(guò)程上的執(zhí)行上下文和代碼簽名,但它們同樣對(duì)大多數(shù)用戶(hù)自定義函數(shù)也支持。
修改執(zhí)行上下文
一般你不想調(diào)用者的許可用來(lái)在破壞的所有權(quán)鏈接接里驗(yàn)證許可。有時(shí)你想代碼好像完全被另一個(gè)用戶(hù)執(zhí)行一樣,通過(guò)另一個(gè)用戶(hù)的許可在訪問(wèn)的所有的對(duì)象上驗(yàn)證許可。這稱(chēng)為切換代碼的執(zhí)行上下文。這讓你使用SQL Server顆粒度許可的優(yōu)點(diǎn),對(duì)潛在的對(duì)象保持完全的許可控制,但還是給不同用戶(hù)執(zhí)行代碼的能力。
在SQL Server里,當(dāng)你定義任何類(lèi)型的用戶(hù)自定義函數(shù)(行內(nèi)表值函數(shù)除外),存儲(chǔ)過(guò)程和觸發(fā)器,你可以使用EXECUTE AS子句作為對(duì)象定義的一部分,表示這個(gè)代碼應(yīng)該在指定用戶(hù)的安全上下文下運(yùn)行。
EXECUTE AS有4個(gè)可用選項(xiàng):
- EXECUTE AS CALLER:默認(rèn)用戶(hù)向下兼容。代碼在調(diào)用者的上下文里執(zhí)行,調(diào)用者必須同時(shí)有執(zhí)行代碼和訪問(wèn)潛在對(duì)象的許可。實(shí)際的操作取決于所有權(quán)鏈接接上是否損壞或完好。
- EXECUTE AS = ‘username’ and EXECUTE AS = ‘loginname’:代碼在指定用戶(hù)或登錄的上下文里運(yùn)行,因此指定的用戶(hù)或登錄必須在所有的潛在對(duì)象上有許可。在這個(gè)情況下,調(diào)用者必須滿(mǎn)足下列之一:
- 在代碼上有EXECUTE許可
- 是sysadmin或db_owner,或者在服務(wù)器或數(shù)據(jù)庫(kù)上有CONTROL SERVER許可,或者對(duì)于用戶(hù)有模仿(impersonate)許可。
使用用戶(hù)名的EXECUTE AS只能應(yīng)用于服務(wù)器范圍的DDL觸發(fā)器,且要登錄到觸發(fā)器。否則,提供的用戶(hù)名必須是有效的數(shù)據(jù)庫(kù)用戶(hù)名稱(chēng)。
- EXECUTE AS SELF:這是創(chuàng)建存儲(chǔ)過(guò)程的當(dāng)前用戶(hù)的縮寫(xiě)。和EXECUTE = [myUserName]等效。SQL Server目錄村里寫(xiě)代碼的實(shí)際用戶(hù)ID。
- EXECUTE AS OWNER:這是在指定用戶(hù)的安全上下文運(yùn)行的另一個(gè)變體,在這個(gè)情況下,代碼所有者在代碼執(zhí)行時(shí)間,而不是在創(chuàng)建時(shí)間。如果在數(shù)據(jù)庫(kù)里,擁有者在代碼創(chuàng)建后修改了,這表示代碼會(huì)在和首次創(chuàng)建代碼的不同用戶(hù)的許可執(zhí)行。
當(dāng)你在SSMS里運(yùn)行代碼時(shí),在會(huì)話(huà)的執(zhí)行上下文里,有兩種EXECUTE AS的變體可以作為語(yǔ)句使用。它們是EXECUTE AS LOGIN = ‘loginname’ 和EXECUTE AS USER = ‘username’。當(dāng)用戶(hù)登錄到SQL Server實(shí)例時(shí),會(huì)話(huà)開(kāi)始,那個(gè)時(shí)候的執(zhí)行上下文設(shè)置為登錄的用戶(hù),用作許可檢查。EXECUTE AS 修改會(huì)話(huà)期間執(zhí)行上下文,直到用戶(hù)執(zhí)行了REVERT語(yǔ)句。
通過(guò)EXECUTE AS修改安全上下文的任何時(shí)間,代碼創(chuàng)建者或會(huì)話(huà)用戶(hù)在語(yǔ)句里指定的用戶(hù)必須有模仿(impersonate)許可。你永遠(yuǎn)不需要模擬自己的許可,例如EXECUTE AS SELF。
使用EXECUTE AS子句
在數(shù)據(jù)庫(kù)里,假設(shè)你有Vendor表。表在SchemaUserTable架構(gòu)里定義,屬于UserTable用戶(hù)。代碼6.1定義了范文這個(gè)表的存儲(chǔ)過(guò)程。在SchemaUserProc定義的存儲(chǔ)過(guò)程,屬于UserProc用戶(hù)。因?yàn)楸砗痛鎯?chǔ)過(guò)程在屬于不同用戶(hù)的不同架構(gòu)里定義,存在斷開(kāi)的所有權(quán)鏈接接。
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:創(chuàng)建在一個(gè)在一個(gè)架構(gòu)里訪問(wèn)不同架構(gòu)里的表的存儲(chǔ)過(guò)程,這里架構(gòu)有不同的擁有者。
提示:
下面的代碼會(huì)創(chuàng)建登錄、數(shù)據(jù)庫(kù)、用戶(hù)和這個(gè)部分使用的架構(gòu),同樣也會(huì)在Vendor表里插入一些記錄。先運(yùn)行下列帶代碼再運(yùn)行代碼6.1。
代碼6.2在存儲(chǔ)過(guò)程上授予EXECUTE許可給真正的用戶(hù),RealUser,它會(huì)運(yùn)行代碼。
1 -- Grant permissions on the stored procedure 2 GRANT EXECUTE ON SchemaUserProc.VendorAccessProc TO RealUser; 3 GO代碼6.2:在新的存儲(chǔ)過(guò)程上授予EXECUTE許可。
在SSMS里,你可以運(yùn)行EXECUTE AS作為臨時(shí)修改安全上下文的在查詢(xún)窗體里運(yùn)行的代碼語(yǔ)句。使用代碼6.3修改安全上下文為RealUser來(lái)運(yùn)行存儲(chǔ)過(guò)程獲得在位于阿拉斯加的供應(yīng)商列表。
1 EXECUTE AS user = 'RealUser'; 2 EXEC SchemaUserProc.VendorAccessProc 'AK';代碼6.3:修改執(zhí)行上下文,以RealUser運(yùn)行存儲(chǔ)過(guò)程。
執(zhí)行這個(gè)代碼引起了下列錯(cuò)誤:
在對(duì)象Vendor上,SELECT許可被拒絕,數(shù)據(jù)庫(kù) 'ExecuteContextDB',架構(gòu) 'SchemaUserTable'。
問(wèn)題是所有權(quán)鏈接接斷開(kāi)了——存儲(chǔ)過(guò)程的所有者和表的所有者不同——RealUser在Vendor表上沒(méi)有SELECT許可。這里有SQL Server如何從概念上分析情況:
- 調(diào)用者是RealUser,它有EXECUTE許可。通過(guò)!
- 存儲(chǔ)過(guò)程的所有者是UserProc。表的所有者是UserTable。這表示有斷開(kāi)的所有權(quán)鏈接接,因此檢查調(diào)用者RealUser,在代碼里有進(jìn)行操作的許可。
- RealUser在Vendor表上沒(méi)有SELECT許可,拋出錯(cuò)誤。失敗!
你可以在存儲(chǔ)過(guò)程定義里使用EXECUTE AS子句來(lái)修正問(wèn)題,假設(shè)你是存儲(chǔ)過(guò)程的創(chuàng)建者,在這個(gè)情況里想允許RealUser運(yùn)行代碼。首先,使用代碼6.1顯示的REVERT語(yǔ)句來(lái)撤銷(xiāo)安全上下文的RealUser切換,返回你自己的安全上下文:
1 REVERT;代碼6.4:恢復(fù)用戶(hù)運(yùn)行SSMS的原始安全上下文
接下來(lái),修改存儲(chǔ)過(guò)程來(lái)包含EXECUTE AS子句使用UserTable的安全上下文來(lái)運(yùn)行存儲(chǔ)過(guò)程,它在表上擁有SELECT許可,如代碼6.5所示。
代碼6.5:修改存儲(chǔ)過(guò)程,在運(yùn)行時(shí)間使用EXECUTE AS修改運(yùn)行上下文
提示:
在這個(gè)例子里,UserTable通過(guò)成員資格擁有在Vendor表上的SELECT許可。但成員資格沒(méi)有必要用來(lái)執(zhí)行上下文切換工作。例如,可以通過(guò)表?yè)碛姓呤谟栌脩?hù)EXECUTE AS user許可。
然后修改安全上下文為UserTable,再次嘗試運(yùn)行存儲(chǔ)過(guò)程,使用代碼6.6。
1 EXECUTE AS user = 'RealUser'; 2 EXEC SchemaUserProc.VendorAccessProc 'AK'; 3 REVERT;代碼6.6:測(cè)試修改后的存儲(chǔ)過(guò)程來(lái)看看RealUser現(xiàn)在能否執(zhí)行代碼。
這次調(diào)用成功,因?yàn)楫?dāng)SQL Server在所有權(quán)鏈接接檢查許可時(shí)——還是斷開(kāi)的——它發(fā)現(xiàn)UserTable有需要的SELECT許可。結(jié)果如插圖6.1所示。
插圖6.1:在不同的用戶(hù)安全上下文下運(yùn)行存儲(chǔ)過(guò)程的結(jié)果
代碼簽名
使用EXECUTE AS子句修改T-SQL代碼段的安全上下文只是解決斷開(kāi)所有權(quán)鏈接接問(wèn)題的一種方法。另一個(gè)方法是使用證書(shū)或非對(duì)稱(chēng)匙的簽名代碼。這個(gè)技術(shù)授予代碼本身許可,不需要你修改運(yùn)行上下文或取決于調(diào)用者的許可。而且通過(guò)證書(shū)的使用或非對(duì)稱(chēng)匙的嚴(yán)格控制,你還是可以控制哪個(gè)主體能利用運(yùn)行代碼的許可。
這個(gè)方式的方法是你創(chuàng)建一個(gè)安全,加密的證書(shū)或非對(duì)稱(chēng)匙,然后創(chuàng)建與證書(shū)或匙關(guān)聯(lián)的用戶(hù)。這是特別的用戶(hù)類(lèi)型,不關(guān)聯(lián)登錄。你分配需要的許可來(lái)運(yùn)行到用戶(hù)的存儲(chǔ)過(guò)程,然后使用ADD SIGNATURE語(yǔ)句分配證書(shū)或匙到存儲(chǔ)過(guò)程。存儲(chǔ)過(guò)程使用證書(shū)或匙關(guān)聯(lián)的用戶(hù)許可。
即使存儲(chǔ)過(guò)程使用EXECUTE AS語(yǔ)句修改執(zhí)行上下文,你還是可以使用這個(gè)技術(shù)。代碼簽名的常見(jiàn)情形是,修改執(zhí)行上下文到代碼需要執(zhí)行的最多許可的運(yùn)行上下文,然后使用代碼簽名添加一個(gè)或多個(gè)額外許可。
和往常一樣,需要通過(guò)實(shí)例來(lái)演示下這個(gè)技術(shù)。代碼6.7創(chuàng)建再次從ExecuteContextDB數(shù)據(jù)庫(kù)的Vendor表獲取數(shù)據(jù)。UnsignedProc存儲(chǔ)過(guò)程沒(méi)有簽名,因此當(dāng)RealUser運(yùn)行它的時(shí)候會(huì)失敗。SignedProc存儲(chǔ)過(guò)程會(huì)簽名,對(duì)于RealUser用戶(hù)會(huì)正常運(yùn)行。
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:創(chuàng)建唯一的存儲(chǔ)過(guò)程并對(duì)RealUser在它們上面授予EXECUTE許可
但這次,我們不是通過(guò)修改運(yùn)行上下文授予SELECT許可,我們會(huì)創(chuàng)建一個(gè)證書(shū),如代碼6.8所示。代碼然后創(chuàng)建從證書(shū)創(chuàng)建一個(gè)用戶(hù),對(duì)用戶(hù)在Vendor表上授予SELECT許可。最后,代碼使用ADD SIGNATURE語(yǔ)句添加證書(shū)到SignedProc存儲(chǔ)過(guò)程。注意只有SignedProc拿到簽名;UnsignedProc還是沒(méi)簽名。
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:實(shí)現(xiàn)證書(shū)和分配許可到代碼的代碼
最后,到測(cè)試簽名架構(gòu)代碼的時(shí)候了,如代碼6.9所示。結(jié)果如插圖6.2所示,UnsignedProc有斷開(kāi)的所有權(quán)鏈接接,RealUser在Vendor表上沒(méi)有SELECT許可,因此執(zhí)行失敗。SignedProc通過(guò)使用代碼簽名授予SELECT許可,成功執(zhí)行返回三條阿拉斯加的數(shù)據(jù)。
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:測(cè)試使用證書(shū)簽名的代碼
插圖6.2:測(cè)試在UnsignedProc和SignedProc存儲(chǔ)過(guò)程代碼簽名的結(jié)果
配置這個(gè)有點(diǎn)復(fù)雜,但安全上受益非常值得。使用正確的話(huà),這個(gè)技術(shù)剔除了用戶(hù)在潛在對(duì)象上需要的SELECT許可和存儲(chǔ)過(guò)程上的EXECUTE許可。很可能這不是你在存儲(chǔ)過(guò)程或用戶(hù)自定義函數(shù)里廣泛使用的,但當(dāng)處理斷開(kāi)的所有權(quán)鏈接接且沒(méi)有方便的擁有需要許可的主體時(shí),它漂亮的解決了一些安全問(wèn)題。
小結(jié)
在SQL Server里創(chuàng)建存儲(chǔ)過(guò)程和用戶(hù)自定義函數(shù)的最簡(jiǎn)單方法是用完整的所有權(quán)鏈接接來(lái)是實(shí)現(xiàn)它們,代碼的所有者也擁有代碼訪問(wèn)的所有數(shù)據(jù)庫(kù)對(duì)象。但通常這并不可行,當(dāng)在SQL Server實(shí)例里對(duì)象的成員資格在多個(gè)主體間分發(fā)的時(shí)候。這篇文件介紹了你可以處理斷開(kāi)的所有權(quán)鏈接接的兩個(gè)技術(shù),通過(guò)修改執(zhí)行上下文和使用代碼簽名分配許可。這些技術(shù)是免費(fèi)贈(zèng)送的,因此你可以對(duì)單個(gè)存儲(chǔ)過(guò)程或函數(shù)一起使用它們。那樣的話(huà),你可以處理你面對(duì)的任何許可架構(gòu),來(lái)保持你的數(shù)據(jù)庫(kù)及它的數(shù)據(jù)庫(kù)盡可能的安全。
原文鏈接
http://www.sqlservercentral.com/articles/Stairway+Series/121476/
總結(jié)
以上是生活随笔為你收集整理的SQL Server安全(6/11):执行上下文与代码签名(Execution Context and Code Signing)的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: .Net下几种日志管理方法
- 下一篇: Linux 面试最常问的十个问题