儲存過程可以選擇和更新其他數據庫中的表 - 授予最小權限
我最近不得不製作儲存過程spPartyOrderAllocation_AllocateItems來選擇和更新其他數據庫中的表,同時將權限級別保持在最低水平。
這是必要的,因為特定應用程序Stock Allocation將連接到數據庫並僅執行此儲存過程。
我這樣做的方法是:
**1)**創建一個登錄名,用於在不同數據庫中執行儲存過程
USE [master] GO CREATE LOGIN [PartyOrderAlloc_HiddenLogin] WITH PASSWORD=N'R4dh3R4dh3' , DEFAULT_DATABASE=[master], CHECK_EXPIRATION=OFF, CHECK_POLICY=ON GO
**2)**將上述登錄的模擬授予將執行該過程的使用者組
GRANT IMPERSONATE ON LOGIN::[PartyOrderAlloc_HiddenLogin] TO [mycompany\it testing team] GO
**3)**根據上面(1)的登錄創建使用者並授予所需的所有權限
USE [SAOrder] GO CREATE USER [PartyOrderAlloc_HiddenUser] FOR LOGIN [PartyOrderAlloc_HiddenLogin] GO --The SELECT permission was denied on the object 'tblSAOrderItemStatus', database 'SAOrder', schema 'dbo'. GRANT SELECT ON dbo.tblSAOrderItemStatus TO [PartyOrderAlloc_HiddenUser] GO -- The UPDATE permission was denied on the object 'tblSAOrderItemStatus', database 'SAOrder', schema 'dbo'. GRANT UPDATE ON dbo.tblSAOrderItemStatus TO [PartyOrderAlloc_HiddenUser] GO USE [SAStockData] GO CREATE USER [PartyOrderAlloc_HiddenUser] FOR LOGIN [PartyOrderAlloc_HiddenLogin] GO GRANT SELECT ON dbo.xtblOrgUnt TO [PartyOrderAlloc_HiddenUser] GO GRANT INSERT ON dbo.tblAudit TO [PartyOrderAlloc_HiddenUser] GO USE [SAStockLevel] GO CREATE USER [PartyOrderAlloc_HiddenUser] FOR LOGIN [PartyOrderAlloc_HiddenLogin] GO GRANT SELECT ON dbo.tblOrgGrpStockLevel TO [PartyOrderAlloc_HiddenUser] GO GRANT UPDATE ON dbo.tblOrgGrpStockLevel TO [PartyOrderAlloc_HiddenUser] GO GRANT SELECT ON dbo.tblOrgUntStockLevel TO [PartyOrderAlloc_HiddenUser] GO GRANT UPDATE ON dbo.tblOrgUntStockLevel TO [PartyOrderAlloc_HiddenUser] GO GRANT SELECT ON dbo.ztblAllocationBlockItems TO [PartyOrderAlloc_HiddenUser] GO
**4)**在數據庫上創建儲存過程
$$ SAOrder $$
use SAOrder go alter PROCEDURE spPartyOrderAllocation_AllocateItems(@strBxOrderNo varchar(20)) --with EXECUTE AS 'PartyOrderAlloc_HiddenUser' AS BEGIN -- 18-Feb-2016 BAC Created spPartyOrderAllocation_AllocateItems -- SET NOCOUNT ON; EXECUTE AS LOGIN='PartyOrderAlloc_HiddenLogin' IF EXISTS (SELECT * FROM tempdb..sysobjects WHERE id=OBJECT_ID('tempdb..#YTAB')) DROP TABLE #YTAB Declare @sintOrderSeqNo smallint Declare @strItemNo varchar(20) Declare @tintOrgGrpId tinyint Declare @tintOrgUntId tinyint --Select oders items that are on allocation hold and have stock in the W/H Select OIS.strBxOrderNo, OIS.sintOrderSeqNo, OIS.strItemNo, SL.tintOrgGrpId, Null tintOrgUntId_Target Into #YTAB From SAOrder.dbo.tblSAOrderItemStatus OIS Join SAStockLevel.dbo.tblOrgGrpStockLevel SL on SL.bigOrgGrpStkLvlId = OIS.bigOrgGrpStkLvlId Join SAStockLevel.dbo.ztblAllocationBlockItems ABI on ABI.strItemNo = OIS.strItemno and ABI.tintOrgGrpId = SL.tintOrgGrpId Where @strBxOrderNo = OIS.strBxOrderNo And IsNull(OIS.sintAllocToOrgUntId,0) = 0 And OIS.sintSAItemStatus = 1 And SL.tintStockTypeId = 1 If @@ROWCOUNT = 0 Begin Select '1' Return End Declare cur_ytbIED_SAOrderAllocationReplenishment cursor forward_only for Select strBxOrderNo , sintOrderSeqNo , strItemNo , tintOrgGrpId From #YTAB Begin Transaction Begin Try Open cur_ytbIED_SAOrderAllocationReplenishment Fetch Next From cur_ytbIED_SAOrderAllocationReplenishment Into @strBxOrderNo , @sintOrderSeqNo , @strItemNo , @tintOrgGrpId While @@FETCH_STATUS = 0 Begin Begin Select Top 1 @tintOrgUntId = tintOrgUntId From SAStockLevel.dbo.tblOrgUntStockLevel OUSL Where strItemNo = @strItemNo And tintOrgUntId In (Select tintOrgUntId from SAStockData.dbo.xtblOrgUnt Where tintOrgGrpId = @tintOrgGrpId and tintOrgUntId<>1) And lngTotal > (Case When lngAllocated > 0 Then lngAllocated Else 0 End) Order By lngTotal - (Case When lngAllocated > 0 Then lngAllocated Else 0 End) Desc IF @@ROWCOUNT = 0 Begin Delete #YTAB Where strBxOrderNo = @strBxOrderNo and sintOrderSeqNo = @sintOrderSeqNo End Else Begin -- Reserve stock in the W/H by updating allocated stock on w/h row print 'tintOrgUntId = ' + convert(varchar(3),@tintOrgUntId) Update SAStockLevel.dbo.tblOrgUntStockLevel Set lngAllocated = lngAllocated + 1 Where strItemNo = @strItemNo And tintOrgUntId = @tintOrgUntId -- Update y-table if w/h allocated to Update #YTAB Set tintOrgUntId_Target = @tintOrgUntId Where strBxOrderNo = @strBxOrderNo AND sintOrderSeqNo = @sintOrderSeqNo -- Allocated to W/H so now insert to log indicating this has happened, In case we require to roll it back later INSERT INTO tblSAOrderAllocationItemAllocateLog ([strItemNo] ,[tintOrgUntId] ,[sintQuantity]) VALUES (@strItemNo ,@tintOrgUntId ,1) End End Fetch Next from cur_ytbIED_SAOrderAllocationReplenishment Into @strBxOrderNo , @sintOrderSeqNo , @strItemNo , @tintOrgGrpId End Close cur_ytbIED_SAOrderAllocationReplenishment Deallocate cur_ytbIED_SAOrderAllocationReplenishment --Move the stock allocated from reserved to allocated UPDATE SAStockLevel.dbo.tblOrgGrpStockLevel SET lngReserved = lngReserved - sintQuantity ,lngAllocated = lngAllocated + sintQuantity FROM SAStockLevel.dbo.tblOrgGrpStockLevel GSL JOIN ( SELECT yOAR.strItemNo , OU.tintOrgGrpId , COUNT (*) AS sintQuantity FROM #YTAB yOAR JOIN SAStockData.dbo.xtblOrgUnt OU on OU.tintOrgUntId = yOAR.tintOrgUntId_Target GROUP BY yOAR.strItemNo,OU.tintOrgGrpId ) ALog ON ALog.strItemNo = GSL.strItemNo AND ALog.tintOrgGrpId = GSL.tintOrgGrpId AND GSL.tintStockTypeId = 1 UPDATE tblSAOrderItemStatus SET sintAllocToOrgUntId = tintOrgUntId_Target FROM tblSAOrderItemStatus OIS JOIN #YTAB OA ON OA.strBxOrderNo = OIS.strBxOrderNo AND OA.sintOrderSeqNo = OIS.sintOrderSeqNo INSERT INTO SAStockData.dbo.tblAudit(dtmDateTime,strMessage) VALUES (Getdate(),'Manual Allocation Completed Successful') End Try Begin Catch Select Error_Message() IF @@TRANCOUNT > 0 Rollback Return End Catch commit Select '0' END GO
這一直工作得很好。結果和行為符合預期。
題:
**1)**我怎樣才能以不同的方式更有效地做到這一點?
**2)**你有沒有註意到,而不是用 EXECUTE 編寫程序,因為我不得不添加這一行
EXECUTE AS LOGIN='PartyOrderAlloc_HiddenLogin'
我知道…我也不喜歡它,但它不適用於我使用 EXECUTE AS。
**3)**也許如果我的登錄名和使用者都是域帳戶,這會有所不同嗎?
我不想添加任何數據庫所有權鏈,或者為了這項工作值得信賴,除非真的有必要。
還:
我們仍然在這些測試機器上使用 sql-2005:
Microsoft SQL Server 2005 - 9.00.5000.00 (X64) 2010 年 12 月 10 日 10:38:40 版權所有 (c) 1988-2005 Microsoft Corporation Developer Edition (64-bit) on Windows NT 6.0 (Build 6002: Service Pack 2)
1)我怎樣才能以不同的方式更有效地做到這一點?
這可以通過使用我稱之為“基於程式碼”的安全性而不是正常的“基於登錄/使用者”的安全性來更好地處理。這個想法本質上是授予一個或多個模組(儲存過程、觸發器、大多數類型的函式等)而不是登錄或使用者的權限。然後,您
EXECUTE
將模組的權限授予任何合適的使用者和/或角色。這是一種更簡潔的方法,因為它更加精細/可控,並且不需要
IMPERSONATE
權限EXECUTE AS
、跨數據庫所有權連結或TRUSTWORTHY ON
. 您現有的登錄名/使用者只能執行編碼到這些模組中的非常具體的事情,而授予他們IMPERSONATE
為在此預期目的之外使用該權限打開了大門。而且這也更安全,因為 usingEXECUTE AS
也可以在此預期目的之外使用,並且如果有人要更改儲存過程的定義,它仍然有效。相反,當您對模組進行簽名時,簽名是根據該模組的目前定義生成的,包括語句的WITH EXECUTE AS
子句CREATE
。意思是,任何更改模組定義或principal_id
指定的 forWITH EXECUTE AS
將使簽名無效,因此將自動刪除簽名,導致不再授予隱含權限。您可以通過創建非對稱密鑰或證書**來執行此操作,然後根據需要根據該密鑰或證書創建登錄名和/或使用者,然後使用相同的密鑰或證書對應該是的模組進行簽名能夠做登錄的使用者不應該做的事情。最後,您根據需要將所需的權限分配給登錄和/或使用者。
我在以下答案中提供了幾個範例和解釋:
- 是否可以將證書添加到數據庫角色?
- 由證書籤名的儲存過程的 SSDT 模式比較
- 我需要向使用者提供哪些最低權限才能檢查 SQL Server 代理服務的狀態?
- SQL Server 鏈權限
- 查看 SQL Server 作業日誌的角色
- 為 sysmail_update_account_sp 分配權限
2)你有沒有註意到,而不是用 EXECUTE 編寫程序,因為我不得不添加這一行
這是因為, , etc 語句的
EXECUTE AS
子句僅適用於每個數據庫本地的使用者。登錄是伺服器級別的。這與實施我在#1 中的建議無關。CREATE PROCEDURE``CREATE FUNCTION
3)也許如果我的登錄名和使用者都是域帳戶,這會有所不同嗎?
那不應該有什麼不同。但同樣,這與實施我在 #1 中的建議無關。
我不想添加任何數據庫所有權鏈,或者為了這項工作值得信賴,除非真的有必要。
當使用 #1 中建議的基於簽名/基於程式碼的安全性時,這些都不是必需的。
** 證書(包含密鑰)和非對稱密鑰之間的主要區別在於將該密鑰複製到其他數據庫的難易程度。如果需要非常本地化到單個數據庫,那麼非對稱密鑰就可以了。但是,如果您需要跨多個數據庫進行管理,或者需要伺服器級別的權限,那麼使用證書通常更容易,因為它們允許將其密鑰資訊備份到一個文件中,然後可以使用該文件重新創建該文件其他數據庫中的相同證書,即使在其他實例上也是如此。從 SQL Server 2012 開始,您可以提取可用於重新創建它們的十六進製字節。對於非對稱密鑰,我發現在所有數據庫中使用一緻密鑰創建它們的唯一方法是將密鑰資訊嵌入到程序集中,然後將該程序集載入到數據庫中,從程序集中創建非對稱密鑰,然後如果不再需要程序集,請將其刪除。這是使用 SQLCLR 時的一種自然方法,因為您已經有一個程序集並且應該對其進行簽名(使用密碼 -
.pfx
文件代替.snk
)。但是當不使用 SQLCLR 時,只需使用證書即可。