使用 EXECUTE AS 擴充資料庫模擬

SQL Server 可支援模擬其他主體的功能,您可以明確地使用獨立的 EXECUTE AS 陳述式,也可以在模組上隱含地使用 EXECUTE AS 子句。獨立的 EXECUTE AS 陳述式可用來模擬伺服器層級的主體,或使用 EXECUTE AS LOGIN 陳述式來模擬登入。獨立的 EXECUTE AS 陳述式也可用來模擬資料庫層級的主體,或是使用 EXECUTE AS USER 陳述式來模擬使用者。

藉由 EXECUTE AS 子句在模組上執行的隱含模擬,會以資料庫或伺服器層級來模擬指定的使用者或登入。這種模擬取決於模組是資料庫層級的模組 (例如,預存程序或函數),還是伺服器層級的模組 (例如,伺服器層級的觸發程序)。

了解模擬範圍

使用 EXECUTE AS LOGIN 陳述式來模擬主體時,或是使用 EXECUTE AS 子句在伺服器範圍的模組中模擬主體時,模擬的範圍為整個伺服器。這表示在內容切換之後,即可在模擬登入具有權限的伺服器中,存取任何資源。

然而,使用 EXECUTE AS USER 陳述式來模擬主體時,或是使用 EXECUTE AS 子句在資料庫範圍的模組中模擬主體時,依預設,模擬的範圍被限制在該資料庫。這表示若要參考資料庫範圍以外的物件,將會傳回錯誤。若要了解此預設行為的原因,請考量下列狀況。

資料庫擁有者可能會有該資料庫的完整權限,但是在該資料庫的範圍之外,卻沒有任何權限。因此,SQL Server 不允許資料庫擁有者模擬 (或讓其他人可以模擬) 另一位使用者,來存取超出資料庫擁有者現行權限範圍的資源。

例如,試想主機環境中有兩個資料庫,而這兩個資料庫分別屬於不同的擁有實體。資料庫1 為 Bob 所擁有,而資料庫2 為 Fred 所擁有。Bob 和 Fred 都不想讓其他人存取其各自資料庫中的資源。身為資料庫1 的擁有者,Bob 可以在其資料庫中為 Fred 建立一個使用者,另外,由於 Bob 擁有資料庫 1 的完整權限,所以他也可以模擬使用者 Fred。然而,因為 SQL Server 採用安全限制的關係,Bob 不能在模擬的內容下存取 Fred 的資料庫。若沒有這些預設限制,Bob 就可以在 Fred 不知情的情況下存取其資料。這就是為什麼在預設的情況下,資料庫層級的模擬範圍會受資料庫所限制。

然而,在某些狀況中,選擇性地將模擬範圍擴充到資料庫以外是有用的。例如,有一個應用程式使用兩個資料庫,並且需要從一個資料庫來存取另一個資料庫,就是屬於這種情形。

試想一個行銷應用程式要在 Marketing 資料庫中叫用預存程序 GetSalesProjections,而該預存程序中定義了一個執行內容切換。預存程序呼叫 Sales 資料庫,以從 SalesStats 資料表中擷取銷售資訊。依預設,這個狀況無法運作,因為在一個資料庫中建立的執行內容,在該資料庫以外是無效的。然而,行銷應用程式的開發人員不想讓行銷應用程式的使用者直接存取 Sales 資料庫,或是擁有該資料庫中任何物件的權限。理想的解決方法是在預存程序中使用 EXECUTE AS 子句,以模擬具有 Sales 資料庫必要權限的使用者。但是現行的預設限制卻不能讓您這麼做。所以問題就是開發人員要如何解決這個問題。

在 SQL Server 中,您可以在那兩個資料庫之間建立一個信任模式,選擇性地擴充在一個資料庫中建立的資料庫模擬範圍。但是,在說明這個信任模式以及要如何選擇性地擴充模擬範圍之前,您應先了解 SQL Server 中的驗證和驗證器角色。

了解驗證器

驗證是由特定主體建立身分,並向系統證明其身分的程序。驗證器是驗證特定主體真實性或為其做擔保的實體。例如,與 SQL Server 進行連接時,會由 SQL Server 執行個體來驗證針對該連接所建立的登入資料。

假設使用者要使用 EXECUTE AS LOGIN 陳述式,在伺服器層級明確地切換內容,則需要伺服器層級的模擬權限。這些權限可讓被授與權限者 (呼叫 EXECUTE AS LOGIN 陳述式的人),能夠模擬 SQL Server 執行個體中任何地方的指定登入。事實上,該陳述式可以讓呼叫者模擬所模擬之登入的登入動作。伺服器層級範圍的權限擁有者是擁有 SQL Server 執行個體的系統管理員 (sysadmin)。在這個伺服器層級模擬案例中,驗證器為系統管理員 (sysadmin) 或是 SQL Server 本身的執行個體。

但是因為資料庫範圍模組上的 EXECUTE AS USER 陳述式或 EXECUTE AS 子句,您還要考慮已經建立內容的情況。在這些案例中,會檢查資料庫範圍內的模擬權限。使用者的 IMPERSONATE 權限預設範圍為資料庫本身,其擁有者為 dbo。這些模擬的驗證器也是資料庫擁有者。此外,那事實上也是建立模擬使用者身分及擔保其驗證的資料庫擁有者。由於資料庫擁有者擁有完整的資料庫,所以在該特定資料庫中的任何地方,模擬的內容都會被視為有效的。但是在該資料庫以外,模擬的內容即無效。

如何使用驗證器

驗證器可用來決定所建立的內容在特定的範圍中是否有效。通常驗證器不是系統管理員 (SA) 就是 SQL Server 的執行個體,或如果包含資料庫,那就是 dbo。驗證器其實就是為特定使用者或登入建立內容所在之範圍的擁有者。此驗證器資訊會被擷取在為登入及使用者維護的 Token 資訊中,並可透過 sys.user_tokensys.login_token 檢視來查看。如需詳細資訊,請參閱<瞭解執行內容>。

[!附註]

如果 Token 檢視中沒有傳回驗證器資訊,則該驗證器為 SQL Server 的執行個體。在沒有內容切換,或是在伺服器層級進行模擬的情況下,都是這種情形。

執行內容的範圍擁有者若是其驗證器,則在那整個範圍中,執行內容都是有效的。這是因為範圍內的所有實體,都隱含地信任該範圍 (例如資料庫) 的擁有者。該內容在其他範圍 (例如,其他資料庫或 SQL Server 本身的執行個體) 中也是有效的,在這些範圍中,驗證器都會受到「信任」。因此,超出資料庫範圍的模擬使用者內容是否有效,需視內容驗證器在目標範圍內是否受到信任而定。若目標範圍是另一個資料庫,則要授與驗證器 AUTHENTICATE 權限,以建立信任;若目標範圍是 SQL Server 的執行個體,則要授與驗證器 AUTHENTICATE SERVER 權限,以建立信任。

擴充模擬的範圍

若要將模擬範圍從資料庫中擴充到目標範圍 (例如,另一個資料庫或 SQL Server 的執行個體),則必須符合下列條件。

  • 驗證器必須在目標範圍內受到信任。

  • 來源資料庫必須標示為值得信任。

信任驗證器

以先前的 SalesMarketing 資料庫為例,下列圖例顯示 Marketing 資料庫中的預存程序 GetSalesProjections,如何存取 Sales 資料庫中的 SalesStats 資料表。預存程序包含子句 EXECUTE AS USER MarketingExecSales 資料庫的擁有者是 SalesDBO,而 Marketing 資料庫的擁有者是 MarketingDBO

EXECUTE AS 會切換模組的執行內容

當使用者叫用 GetSalesProjections 預存程序時,EXECUTE AS 子句會隱含地將預存程序的執行內容,從呼叫的使用者切換成 MarketingExec 使用者。此內容的驗證器是 MarketingDBO,也就是 Marketing 資料庫的擁有者。依預設,此程序可存取 Marketing 資料庫中,MarketingExec 使用者所容許存取的任何資源。然而,若要存取 Sales 資料庫中的資料表,Sales 資料庫就必須信任驗證器 MarketingDBO

若要執行此作業,您可以在名為 MarketingDBOSales 資料庫 (對應到 MarketingDBO 登入) 中建立使用者,然後將 Sales 資料庫的 AUTHENTICATE 權限授與該使用者。這樣一來,在該資料庫中,以被授與此權限者作為驗證器的任何執行內容都是有效的。由於驗證器 MarketingDBO 被授與 Sales 資料庫中的 AUTHENTICATE 權限,所以在 Sales 資料庫中,會信任由 Marketing 資料庫中 GetSalesProjections 預存程序之 EXECUTE AS 子句所建立的使用者 MarketingExec

雖然這個範例示範的是擴充模擬的範圍來允許存取外部資料庫中的物件,但是要將模擬範圍擴充至 SQL Server 的執行個體也是有可能的。例如,若該程序是要建立登入 (需要整個伺服器權限的伺服器層級動作),則必須將 AUTHENTICATE SERVER 權限授與內容的驗證器。這表示擁有 AUTHENTICATE SERVER 權限之被授與者作為其驗證器的任何內容會自在 SQL Server 的整個執行個體受信任,而該內容會直接登入 SQL Server 的執行個體。

信任資料庫

在 SQL Server 中,信任模式更進一步提供額外的安全性和資料粒度,以擴充資料庫層級模擬的範圍。您可以使用 AUTHENTICATE 權限來作為讓目標範圍信任內容驗證器的方法,但是您也可以判斷 SQL Server 執行個體是否信任來源資料庫及其內容。

舉例來說,假設 MarketingDBO 主體擁有另一個名為 Conference 的資料庫。並假設 MarketingDBO 希望 Marketing 資料庫中指定的執行內容能夠存取 Sales 資料庫中的資源。然而,但它不想讓 Conference 資料庫中所建立的任何內容存取 Sales 資料庫。

若要達到這項需求,資料庫所包含的模組若是使用模擬內容來存取資料庫以外的資源,則該資料庫必須標示為值得信任。TRUSTWORTHY 屬性可指定 SQL Server 執行個體是否信任資料庫及其內容。TRUSTWORTHY 屬性有兩個目的:

  1. 針對連接到 SQL Server 執行個體,並且可能包含定義在高權限使用者內容下執行之惡意模組的資料庫,可降低其帶來的威脅。

    若要執行此作業,您可以確定所附加的資料庫沒有依預設標示成值得信任。您也可以確定若要透過可能是惡意的模組來存取資料庫外部的資源,則該資料庫必須標示成為值得信任。只有系統管理員 (sysadmin) 固定伺服器角色的成員,才能在資料庫上設定 TRUSTWORTHY 屬性。

  2. 當資料庫具有相同的擁有者,而且在某些範圍中,該擁有者為受信任的驗證器,則可讓 SQL Server 執行個體的系統管理員分辨應該要讓哪些資料庫可以存取外部資源,哪些則不行。

您可以使用 TRUSTWORTHY 屬性來控制此行為。例如,假設您應該要信任來自「資料庫 1」的模擬內容,但不應該信任來自「資料庫 2」的內容,而這兩個資料庫的擁有者是相同的,且在目標範圍中,該擁有者被信任為驗證器。您可以將 database1 的 TRUSTWORTHY 屬性設為 ON,將 database2 的 TRUSTWORTHY 屬性設為 OFF,以確定資料庫 2 中的模組不能存取該資料庫以外的資源。

下圖顯示如何使用 TRUSTWORTHY 資料庫屬性,來控制存取來源資料庫範圍以外的資源。在 Sales 資料庫中,MarketingDBO 被授與 AUTHENTICATE 權限,而且同時是 MarketingConference 資料庫的擁有者。Marketing 資料庫中的 GetSalesProjections 預存程序可以順利地存取 Sales 資料庫,因為它符合兩項安全要求:在目標範圍中,驗證器 MarketingDBO 是被信任的,而且來源資料庫 Marketing 是值得信任的。若要試圖從 Conference 資料庫來存取 Sales 資料庫,將會遭到拒絕,因為只符合一項要求:在目標範圍中,驗證器 MarketingDBO 有受到信任。

控制外部資源的資料庫存取

每當試圖使用模擬內容來存取資料庫範圍以外的資源時,SQL Server 執行個體就會確認提出要求的資料庫是值得信任的,而且驗證器是被信任的。

以憑證及非對稱金鑰作為驗證器

您可以將資料庫擁有者當作驗證器來使用,藉以擴充在資料庫中建立的模擬內容,以存取資料庫範圍以外的資源。若要達成此目的,資料庫擁有者必須受外部資源所信任,而且資料庫本身也必須是值得信任的。然而,這個方式意味著,當受信任的資料庫擁有者被授與目標範圍中的 AUTHENTICATE 或 AUTHENTICATE SERVER 權限,而且執行呼叫的資料庫是值得信任的,在信任該資料庫擁有者的整個目標範圍中,任何在該資料庫中建立的模擬內容都是有效的。

可能需要更細微的信任層級。假設商業要求指定只要信任來源資料庫中的少數模組 (使用 EXECUTE AS 子句來存取目標資源),而不要信任整個來源資料庫。例如,假設 SalesDBO 想要確定只有 GetSalesProjections 預存程序可以用 MarketingExec 使用者的身分來存取 SalesStats 資料表,而不想讓 Marketing 資料庫中每一個具有 MarketingExec 模擬權限的人,來存取 Sales 資料庫中的資源。信任 MarketingDBO 以及將 Marketing 資料庫設為值得信任的並不能完成這項工作。若要提供額外層級的資料粒度來達成該需求,信任模式可讓您將憑證非對稱金鑰當作驗證器來使用。這就需要利用到一種叫做簽章的技術。如需簽章的詳細資訊,請參閱<ADD SIGNATURE (Transact-SQL)>。

使用簽章

模組上的簽章可確保只有可存取用來簽署模組之私密金鑰的人,能夠修改模組中的程式碼。有了簽章程序的保證,您就可以信任簽章中所指定的憑證或非對稱金鑰。更精確地說,就是您可以信任憑證或非對稱金鑰的擁有者,而不僅是資料庫擁有者。

若要信任簽署的模組,您可以在對應到憑證或非對稱金鑰的目標範圍中,將 AUTHENTICATE 或 AUTHENTICATE SERVER 權限授與使用者。

藉由這種方式,在使用授信憑證來簽署的模組中所建立的執行內容,在信任該憑證的目標範圍中就會是有效的。

例如,假設 GetSalesProjections 程序是以名為 C1 的憑證來簽署的。C1 憑證必須位在 Sales 資料庫中,而且使用者 (例如,CertUser1) 必須對應到 C1 憑證。然後就會在 Sales 資料庫中,將 AUTHENTICATE 權限授與 CertUser1

叫用這個程序時,會先確認其簽章,以確保其在簽署時沒有遭到擅改。若已確認簽章,在模組中以 EXECUTE AS 子句來建立的內容,就會以 C1 憑證來作為其驗證器。若沒有確認簽章,就不會將驗證器加入 Token,而且嘗試存取外部資源時,將會失敗。

下圖顯示如何使用簽署的模組,來控制存取來源資料庫範圍以外的資源。Marketing 資料庫中的 GetSalesProjections 程序是使用一個名為 C1 的憑證來簽署的。C1 憑證位於 Sales 資料庫中,而使用者 CertUser 會對應到該憑證。然後就會在 Sales 資料庫中,將 AUTHENTICATE 權限授與 CertUser1

用來限制資料庫存取的憑證

確認此驗證器是否受信任的方式,與資料庫擁有者為驗證器時確認的方式相同。意思就是藉由檢查 AUTHENTICATE SERVER 或 AUTHENTICATE 權限來加以確認。然而,因為該信任是在資料粒度層級建立的,在沒有修改簽章的情況下,無法變更模組,所以就不需要確認資料庫上的 TRUSTWORTHY 屬性。

這樣可以降低因附加含有惡意程式碼的資料庫而帶來的威脅。攻擊者必須以對應於信任憑證的私密金鑰來簽署模組。然而,攻擊者並沒有此金鑰的存取權。此外,如果現有的信任模組經過修改,或是建立了新的模組,該模組就不會有有效的信任簽章。

如需詳細資訊,請參閱<模組簽署 (Database Engine)>。

擴充資料庫模擬範圍的規則

總而言之,唯有在下列條件成立時,在資料庫中建立之內容的模擬範圍才可以擴充至其他範圍:

  • 驗證器 (無論是資料庫擁有者,或是用來簽署模組的憑證或非對稱金鑰) 必須在目標範圍中受到信任。若要達成此條件,您可以將 AUTHENTICATE 或 AUTHENTICATE SERVER 權限授與對應到資料庫擁有者、憑證或非對稱金鑰的主體。

  • 如果驗證器是資料庫擁有者,則來源資料庫必須標示成值得信任。若要達成此條件,您可以將資料庫的 TRUSTWORTHY 屬性設為 ON。

依需求來選取信任機制

無論是資料庫擁有者方法,還是簽章方法,都各有其利弊。適合您的最佳機制需視您的商業需求及商業環境而定。

資料庫擁有者方法

使用資料庫擁有者方法來建立信任的優缺點如下:

  • 不需對加密概念 (例如,憑證或簽章) 有任何了解。

  • 不像簽章型方法有那麼多資料粒度。

  • 將資料庫附加至 SQL Server 執行個體,就會將資料庫上的 TRUSTWORTHY 屬性設為 OFF。必須等系統管理員明確地將 TRUSTWORTHY 屬性設為 ON,資料庫擁有者所信任的模組才會生效。這表示可能需要系統管理員某種程度的介入,所附加的資料庫才能照您的意思來運行,以及存取其他資料庫。

簽章方法

使用簽章方法來建立信任的優缺點如下:

  • 可提供資料粒度層級的信任,但僅適用於在已簽署模組中執行的內容切換。

  • 若是透過獨立陳述式 EXECUTE AS USER 及 EXECUTE AS LOGIN 來建立的內容切換,則無法使用簽章。這兩種陳述式需要以資料庫擁有者型的方法來擴充信任的範圍。

  • 應用程式廠商或開發人員可能會用私密金鑰來簽署模組,但在模組或資料庫出貨之前,就會將私密金鑰移除。這麼做是有效的,因為私密金鑰只是用來簽署模組而已。若是為了要驗證簽章,與模組相關聯的公開金鑰即已足夠。

  • 由於受信任的模組具有簽章,所以不會受到附加資料庫的影響。這些模組不需其他要求即可運作。