web-dev-qa-db-ja.com

実行方法、クロスデータベースクエリ、モジュール署名によるストアドプロシージャのセキュリティ

私はそれを回避することができましたが(再現が示すように)、私には理解できない状況があります。ここがハイポイントです

  • ChainingSourceとChainDestinationの2つのデータベース。どちらもクロスデータベースチェーンがtrueに設定されています。
  • ChainingSourceのストアドプロシージャがEXEC(@sql)を介してChainingDestinationのテーブルにアクセスする
  • ストアドプロシージャはexecute as句で定義されています
  • プロシージャをそのまま実行しようとすると、実行コンテキストのサーバープリンシパルがChainingDestinationにアクセスできないと表示されます
  • そこで、証明書とコード署名をミックスに追加します。つまり、証明書にマップされたログインをサーバーに追加し、ユーザーを各データベースにマップし、それに応じて、証明書にマップされたユーザーに権限を付与します。
  • execute as句をそのままにしておくと、同じエラーが発生します。
  • execute as句を削除すると、すべて問題ありません。

それは私が混乱している最後から2番目のポイントです。または、具体的には、なぜそれが機能せず、最後のものがdoesなのか。


/******************************

            Setup

******************************/
USE [master];
go
IF EXISTS (SELECT 1 FROM [sys].[databases] WHERE [name] = 'ChainingSource')
BEGIN
    ALTER DATABASE [ChainingSource] SET OFFLINE WITH ROLLBACK IMMEDIATE;
    ALTER DATABASE [ChainingSource] SET ONLINE;
    DROP DATABASE [ChainingSource];
END
IF EXISTS (SELECT 1 FROM [sys].[databases] WHERE [name] = 'ChainingDestination')
BEGIN
    ALTER DATABASE [ChainingDestination] SET OFFLINE WITH ROLLBACK IMMEDIATE;
    ALTER DATABASE [ChainingDestination] SET ONLINE;
    DROP DATABASE [ChainingDestination];
END
GO

EXECUTE AS LOGIN = 'sa';
CREATE DATABASE [ChainingSource];
CREATE DATABASE [ChainingDestination];
GO
REVERT;
GO

ALTER DATABASE [ChainingSource] SET DB_CHAINING ON;
ALTER DATABASE [ChainingDestination] SET DB_CHAINING ON;

IF SUSER_ID('myAppUser') IS null
    CREATE LOGIN [myAppUser] WITH password = 'p@ssw0rd!23';

IF SUSER_ID('myAppUserEscalated') IS null
    CREATE LOGIN [myAppUserEscalated] WITH password = 'p@ssw0rd!23';

IF NOT EXISTS (
    SELECT * FROM sys.[symmetric_keys] AS [sk]
    WHERE name = '##MS_DatabaseMasterKey##'
)
BEGIN
    CREATE MASTER KEY ENCRYPTION BY PASSWORD = 'f00bar!23';
    PRINT 'Created master key in databse [master]';
END

IF CERT_ID('myAppCert') IS NULL
    CREATE CERTIFICATE [myAppCert] AUTHORIZATION dbo FROM BINARY = 0x308201BD30820126A003020102021061AF3EB269776BB74629F44629EF9216300D06092A864886F70D0101050500301C311A301806035504031311436F6465205369676E696E6720436572743020170D3136313032303232303932335A180F32303939303130313030303030305A301C311A301806035504031311436F6465205369676E696E67204365727430819F300D06092A864886F70D010101050003818D0030818902818100BCCA7DC1CF2F4874F341AF3C586F0B023CAAD16986ADDAC2F7BFB3BCE590F2A952218F51298067CE3BE9ED695A229DADD029510F0927F30484A587024E0F58EC83924BE49D227D2FE1FCCA0C682528D6A0658AAA6CA5D9F2405AB6950B7D5BA672BB971910D71CEE3B77FF0A4EF59F010AAB445FD127944966C141F7CFC3D5790203010001300D06092A864886F70D010105050003818100011525EDD191767041659701F13F4370F803E6C981A6E33D68863FDACADE709926AB7E3BC8D618EDD07FC52058EFE42D96CA49961CF2936F446EDC4B7D55725FF2F1B37B326D564941CF6A7424551828FE198335AEFA0C892B375D3B676F35B708A48C67F80714643A34050CF9C557FBDD01274BC1ACCCA9A7AD3EC37DD2DA31 WITH PRIVATE KEY (BINARY = 0x1EF1B5B00000000001000000010000001000000054020000CF21B85A9464B60CDAB9F2E419B341490702000000A4000002E67BDF3CE02406E4D69A760D519B3BB6DA77FAFA7D710936EAE5267F072F98A1F7521110EB03955427B79FA386F7D70EDF6E977E92E59761DE0EB186F895AD975CE63C4D8A8B67BA487B9807EDB8B33C7C08EABCCD716E9505170A9729B6E165CE0ED0CA35B5C62D548367FEFC2C694060184D9185331466A0C64A9CB7BF8CA7AC0946A54091A4626978320C7290A784C6147BAF23FD866D9D1D4D1D79DA1B4D2DE213D11299F1417D8C421CC25A2E851FF9CEFA0ECA2006186C787692FDC28F2E702FCC7E76AFBEB95B954B50AA3697E60FC6928392664CE0EDB794AA392C1CC6326102B7CE8A02B367D2F416269DBBF4C16F096780517D32B4653E94DE5C24CE9D39EBC8E6A4EF1B9217F1B4F098F4F77F88CC11C40DDB312BE87CC1430C16AED8773E7691ADE8472BFC02B458B09B40404F61D2E02746AE576582DEE3EC5C09077E127BB4E996A9C4A840E6E0F59D85A3FC4E2844679927DBA6A571927A1F1C938716B8FC922B1D77FAA90BDBA49D1084081E4198A50506C5F6FE87F81B759EE0688428ABA7B2E8CC7D96AC6409DAE41937DB9C1E1CACCD7AE86A8F161316A07B05D523A116AB87022978312EE9853AE9FFA44FFF52114D084934D86D0FFD2D47B974769812BF0F4FE8276FD0DCE4069F11EC3915A68F4454166E3ABAAB9539530117597EE52213FEC7C87254634F10062C5C1D97CE5FEABB13252B22E210F56DB281FC1CE5432A7144FB4B89D00B4F8BC876C8C0F397DB9D22E15E2B07FBB44ADDDFBB6A75728917AC330E3A9F978847AC2D27913B3B6CBF54F1BAEF06072D15050ED1CA7BF9C5763A, DECRYPTION BY PASSWORD = 'f00bar!23')
IF SUSER_ID('myAppCert') IS NULL
    CREATE LOGIN [myAppCert] FROM CERTIFICATE [myAppCert];


USE [ChainingDestination];
CREATE USER [myAppUser];
CREATE USER [myAppUserEscalated];

CREATE MASTER KEY ENCRYPTION BY PASSWORD = 'f00bar!23';
CREATE CERTIFICATE [myAppCert] AUTHORIZATION dbo FROM BINARY = 0x308201BD30820126A003020102021061AF3EB269776BB74629F44629EF9216300D06092A864886F70D0101050500301C311A301806035504031311436F6465205369676E696E6720436572743020170D3136313032303232303932335A180F32303939303130313030303030305A301C311A301806035504031311436F6465205369676E696E67204365727430819F300D06092A864886F70D010101050003818D0030818902818100BCCA7DC1CF2F4874F341AF3C586F0B023CAAD16986ADDAC2F7BFB3BCE590F2A952218F51298067CE3BE9ED695A229DADD029510F0927F30484A587024E0F58EC83924BE49D227D2FE1FCCA0C682528D6A0658AAA6CA5D9F2405AB6950B7D5BA672BB971910D71CEE3B77FF0A4EF59F010AAB445FD127944966C141F7CFC3D5790203010001300D06092A864886F70D010105050003818100011525EDD191767041659701F13F4370F803E6C981A6E33D68863FDACADE709926AB7E3BC8D618EDD07FC52058EFE42D96CA49961CF2936F446EDC4B7D55725FF2F1B37B326D564941CF6A7424551828FE198335AEFA0C892B375D3B676F35B708A48C67F80714643A34050CF9C557FBDD01274BC1ACCCA9A7AD3EC37DD2DA31 WITH PRIVATE KEY (BINARY = 0x1EF1B5B00000000001000000010000001000000054020000CF21B85A9464B60CDAB9F2E419B341490702000000A4000002E67BDF3CE02406E4D69A760D519B3BB6DA77FAFA7D710936EAE5267F072F98A1F7521110EB03955427B79FA386F7D70EDF6E977E92E59761DE0EB186F895AD975CE63C4D8A8B67BA487B9807EDB8B33C7C08EABCCD716E9505170A9729B6E165CE0ED0CA35B5C62D548367FEFC2C694060184D9185331466A0C64A9CB7BF8CA7AC0946A54091A4626978320C7290A784C6147BAF23FD866D9D1D4D1D79DA1B4D2DE213D11299F1417D8C421CC25A2E851FF9CEFA0ECA2006186C787692FDC28F2E702FCC7E76AFBEB95B954B50AA3697E60FC6928392664CE0EDB794AA392C1CC6326102B7CE8A02B367D2F416269DBBF4C16F096780517D32B4653E94DE5C24CE9D39EBC8E6A4EF1B9217F1B4F098F4F77F88CC11C40DDB312BE87CC1430C16AED8773E7691ADE8472BFC02B458B09B40404F61D2E02746AE576582DEE3EC5C09077E127BB4E996A9C4A840E6E0F59D85A3FC4E2844679927DBA6A571927A1F1C938716B8FC922B1D77FAA90BDBA49D1084081E4198A50506C5F6FE87F81B759EE0688428ABA7B2E8CC7D96AC6409DAE41937DB9C1E1CACCD7AE86A8F161316A07B05D523A116AB87022978312EE9853AE9FFA44FFF52114D084934D86D0FFD2D47B974769812BF0F4FE8276FD0DCE4069F11EC3915A68F4454166E3ABAAB9539530117597EE52213FEC7C87254634F10062C5C1D97CE5FEABB13252B22E210F56DB281FC1CE5432A7144FB4B89D00B4F8BC876C8C0F397DB9D22E15E2B07FBB44ADDDFBB6A75728917AC330E3A9F978847AC2D27913B3B6CBF54F1BAEF06072D15050ED1CA7BF9C5763A, DECRYPTION BY PASSWORD = 'f00bar!23')
CREATE USER [myAppCert];
GO


CREATE TABLE [dbo].[topSecret] ([ID] INT IDENTITY, [Secrets] NVARCHAR(100));
INSERT INTO [dbo].[topSecret] ([Secrets]) VALUES ('Nuke Codes!');

GRANT SELECT ON [dbo].[topSecret] TO [myAppUserEscalated];
GRANT SELECT ON [dbo].[topSecret] TO [myAppCert];

GO

USE [ChainingSource];
GO
CREATE USER [myAppUser]
CREATE USER [myAppUserEscalated];

CREATE MASTER KEY ENCRYPTION BY PASSWORD = 'f00bar!23';
CREATE CERTIFICATE [myAppCert] AUTHORIZATION dbo FROM BINARY = 0x308201BD30820126A003020102021061AF3EB269776BB74629F44629EF9216300D06092A864886F70D0101050500301C311A301806035504031311436F6465205369676E696E6720436572743020170D3136313032303232303932335A180F32303939303130313030303030305A301C311A301806035504031311436F6465205369676E696E67204365727430819F300D06092A864886F70D010101050003818D0030818902818100BCCA7DC1CF2F4874F341AF3C586F0B023CAAD16986ADDAC2F7BFB3BCE590F2A952218F51298067CE3BE9ED695A229DADD029510F0927F30484A587024E0F58EC83924BE49D227D2FE1FCCA0C682528D6A0658AAA6CA5D9F2405AB6950B7D5BA672BB971910D71CEE3B77FF0A4EF59F010AAB445FD127944966C141F7CFC3D5790203010001300D06092A864886F70D010105050003818100011525EDD191767041659701F13F4370F803E6C981A6E33D68863FDACADE709926AB7E3BC8D618EDD07FC52058EFE42D96CA49961CF2936F446EDC4B7D55725FF2F1B37B326D564941CF6A7424551828FE198335AEFA0C892B375D3B676F35B708A48C67F80714643A34050CF9C557FBDD01274BC1ACCCA9A7AD3EC37DD2DA31 WITH PRIVATE KEY (BINARY = 0x1EF1B5B00000000001000000010000001000000054020000CF21B85A9464B60CDAB9F2E419B341490702000000A4000002E67BDF3CE02406E4D69A760D519B3BB6DA77FAFA7D710936EAE5267F072F98A1F7521110EB03955427B79FA386F7D70EDF6E977E92E59761DE0EB186F895AD975CE63C4D8A8B67BA487B9807EDB8B33C7C08EABCCD716E9505170A9729B6E165CE0ED0CA35B5C62D548367FEFC2C694060184D9185331466A0C64A9CB7BF8CA7AC0946A54091A4626978320C7290A784C6147BAF23FD866D9D1D4D1D79DA1B4D2DE213D11299F1417D8C421CC25A2E851FF9CEFA0ECA2006186C787692FDC28F2E702FCC7E76AFBEB95B954B50AA3697E60FC6928392664CE0EDB794AA392C1CC6326102B7CE8A02B367D2F416269DBBF4C16F096780517D32B4653E94DE5C24CE9D39EBC8E6A4EF1B9217F1B4F098F4F77F88CC11C40DDB312BE87CC1430C16AED8773E7691ADE8472BFC02B458B09B40404F61D2E02746AE576582DEE3EC5C09077E127BB4E996A9C4A840E6E0F59D85A3FC4E2844679927DBA6A571927A1F1C938716B8FC922B1D77FAA90BDBA49D1084081E4198A50506C5F6FE87F81B759EE0688428ABA7B2E8CC7D96AC6409DAE41937DB9C1E1CACCD7AE86A8F161316A07B05D523A116AB87022978312EE9853AE9FFA44FFF52114D084934D86D0FFD2D47B974769812BF0F4FE8276FD0DCE4069F11EC3915A68F4454166E3ABAAB9539530117597EE52213FEC7C87254634F10062C5C1D97CE5FEABB13252B22E210F56DB281FC1CE5432A7144FB4B89D00B4F8BC876C8C0F397DB9D22E15E2B07FBB44ADDDFBB6A75728917AC330E3A9F978847AC2D27913B3B6CBF54F1BAEF06072D15050ED1CA7BF9C5763A, DECRYPTION BY PASSWORD = 'f00bar!23')
CREATE USER [myAppCert];
GO

CREATE SYNONYM [dbo].[topSecret] FOR [ChainingDestination].[dbo].[topSecret];
GRANT SELECT ON [dbo].[topSecret] TO [myAppUserEscalated];
GRANT SELECT ON [dbo].[topSecret] TO [myAppCert];

GO

IF OBJECT_ID('[dbo].[getSecrets]') IS NOT null
    DROP PROCEDURE [dbo].[getSecrets]
GO

CREATE PROCEDURE [dbo].[getSecrets]
WITH EXECUTE AS 'myAppUserEscalated'
AS
BEGIN

    SELECT * FROM sys.login_token;
    SELECT * FROM sys.user_token;
    EXEC('SELECT * FROM [dbo].[topSecret] AS [ts];');
END
GO
GRANT EXECUTE ON [dbo].[getSecrets] TO [myAppUser];
GO

/******************************

            DEMO

******************************/

-- EXECUTE AS clause only
EXECUTE AS LOGIN = 'myAppUser';
GO
EXEC dbo.[getSecrets]
GO
REVERT;
GO

-- no bueno. let's try to add a signature!

ADD SIGNATURE TO [dbo].[getSecrets]
    BY CERTIFICATE [myAppCert];

EXECUTE AS LOGIN = 'myAppUser';
GO
EXEC dbo.[getSecrets]
GO
REVERT;
GO

-- still no bueno. 
-- let's take off the EXECUTE AS clause and sign

ALTER PROCEDURE [dbo].[getSecrets]
AS
BEGIN

    SELECT * FROM sys.login_token;
    SELECT * FROM sys.user_token;
    EXEC('SELECT * FROM [dbo].[topSecret] AS [ts];');
END
GO

ADD SIGNATURE TO [dbo].[getSecrets]
    BY CERTIFICATE [myAppCert];

EXECUTE AS LOGIN = 'myAppUser';
GO
EXEC dbo.[getSecrets]
GO
REVERT;
GO
 -- bueno
5
Ben Thul

あなたは正しい方向に向かっていて、とても近くにいました。ここで、モジュールの署名を、追加されたものとしてではなく、EXECUTE ASの代わりとして表示する必要があります。 EXECUTE ASmyAppUserEscalatedを完全に削除し、モジュール署名(および証明書ベースのログインと関連ユーザー)のみに依存すると、クロスDB権限が許可されますand所有権があるときに権限を維持する- TRUSTWORTHYOFFに設定したまま(およびDB_CHAININGOFFに設定したままでも)、連鎖は機能しません(動的SQLなど)。

以下は、問題のスクリプトに基づくテストスクリプトですが、最小限のオプション(つまり、DB_CHAININGnotがオンになっていること、および証明書と証明書ベースのログイン/ユーザーは作成されません)。また、以下のさまざまな組み合わせを簡単にテストできる4つのストアドプロシージャもあります。

  • デフォルト(偽装なし、動的SQLなし)
  • 偽装(ただし動的SQLは不可)
  • 動的SQL(偽装なし)
  • 偽装と動的SQL

スクリプトには6つのテストがあります。

  • テスト1は、デフォルトでは、どの組み合わせも機能しないことを示しています。ストアドプロシージャgetSecrets(偽装または動的SQLなし)は、所有権の連鎖により、単純なSQLよりも遠くなりますが、クロスDB所有権の連鎖がないため、他のDBにアクセスできません。ダイナミックSQLを使用する2つのストアドプロシージャは、ダイナミックSQLが所有権チェーンを壊すため、ストレートSQLと同じエラーが発生します。

  • テスト2は、DB_CHAININGのみがONに設定されている場合、ストアドプロシージャgetSecrets(偽装または動的SQLなし)がデータベース間で必要に応じて機能することを示しています。ただし、動的SQLが所有権チェーンを壊すため、getSecretsWithDynamicSqlストアドプロシージャは失敗します。そのため、クロスDB所有権チェーンの恩恵を受けることはできません。

  • テスト3は、TRUSTWORTHYのみがONに設定されている場合(「ソース」DBの場合のみ)、動的SQLの有無にかかわらず、偽装を使用したコード(つまりEXECUTE AS)を示しています。データベース間で必要に応じて機能します。ただし、テスト1と同様に、偽装を使用しないコードは機能しません。もちろん、セキュリティ上のリスクがあるため、TRUSTWORTHYONにしたくありません。このテストは、モジュール署名の前の状態を示すためのものです(つまり、偽装を使用する場合はTRUSTWORTHYが必要であり、動的SQLを使用する場合はTRUSTWORTHYが必要です)。

  • テスト4は、両方のDB_CHAININGandONTRUSTWORTHYに設定されている場合、動的SQLを使用しないコードは偽装を必要とせずに機能し、動的SQLがあるかどうかにかかわらず、偽装はデータベース間で必要に応じて機能します。ただし、セキュリティ上のリスクがあるため、ONTRUSTWORTHYにしたくありません。このテストは、モジュール署名の前の状態を示すためのものです。

  • テスト5はDB_CHAININGandOFFTRUSTWORTHYに戻し、証明書と関連するログインとユーザーを作成し、次の2つのストアドプロシージャに署名します- not偽装を使用する(偽装を使用する必要がなくなったため)。署名されたストアドプロシージャは両方とも、意図したとおりに機能します。

  • テスト6では、偽装を使用した2つのストアドプロシージャが削除され、偽装されていた「エスカレーション」ログインと関連ユーザーも削除されます。テスト5を実行すると、必要なのはモジュール署名だけであることが証明されます(これが、アクセス許可を制御するための非常に優れた方法である理由です:-)。

テストスクリプト:

/******************************

            Setup

******************************/

/*************************  CLEANUP *************************************/

USE [master];
GO
IF EXISTS (SELECT 1 FROM [sys].[databases] WHERE [name] = N'ChainingSource')
BEGIN
    PRINT 'Dropping [ChainingSource] DB...';
    ALTER DATABASE [ChainingSource] SET OFFLINE WITH ROLLBACK IMMEDIATE;
    ALTER DATABASE [ChainingSource] SET ONLINE;
    DROP DATABASE [ChainingSource];
END;

IF EXISTS (SELECT 1 FROM [sys].[databases] WHERE [name] = N'ChainingDestination')
BEGIN
    PRINT 'Dropping [ChainingDestination] DB...';
    ALTER DATABASE [ChainingDestination] SET OFFLINE WITH ROLLBACK IMMEDIATE;
    ALTER DATABASE [ChainingDestination] SET ONLINE;
    DROP DATABASE [ChainingDestination];
END;

IF (SUSER_ID(N'myAppUser') IS NOT NULL)
BEGIN
  PRINT 'Dropping [myAppUser] Login...';
  DROP LOGIN [myAppUser];
END;

IF (SUSER_ID(N'myAppUserEscalated') IS NOT NULL)
BEGIN
  PRINT 'Dropping [myAppUserEscalated] Login...';
  DROP LOGIN [myAppUserEscalated];
END;
GO


/*************************  CREATE *************************************/

EXECUTE AS LOGIN = N'sa';
PRINT 'Creating databases...';
CREATE DATABASE [ChainingSource] COLLATE Latin1_General_100_CI_AS_SC;
CREATE DATABASE [ChainingDestination] COLLATE Latin1_General_100_CI_AS_SC;
REVERT;
GO


-- Set up Login/User: [myAppUser]
IF (SUSER_ID(N'myAppUser') IS NULL)
BEGIN
    EXEC(N'
      PRINT ''Creating [myAppUser]...'';
      USE [master];
      CREATE LOGIN [myAppUser] WITH PASSWORD = N''p@ssw0rd!23'';

      USE [ChainingDestination];
      CREATE USER [myAppUser];

      USE [ChainingSource];
      CREATE USER [myAppUser];
     ');
END;

-- Set up Login/User: [myAppUserEscalated]
IF (SUSER_ID(N'myAppUserEscalated') IS NULL)
BEGIN
    EXEC(N'
      PRINT ''Creating [myAppUserEscalated]...'';
      USE [master];
      CREATE LOGIN [myAppUserEscalated] WITH PASSWORD = N''p@ssw0rd!23'';

      USE [ChainingDestination];
      CREATE USER [myAppUserEscalated];

      USE [ChainingSource];
      CREATE USER [myAppUserEscalated];
     ');
END;
GO


USE [ChainingDestination];

CREATE TABLE [dbo].[topSecret] ([ID] INT IDENTITY, [Secrets] NVARCHAR(100));
INSERT INTO [dbo].[topSecret] ([Secrets]) VALUES (N'Nuke Codes!');

GRANT SELECT ON [dbo].[topSecret] TO [myAppUserEscalated];
GO


USE [ChainingSource];

CREATE SYNONYM [dbo].[topSecret] FOR [ChainingDestination].[dbo].[topSecret];

GRANT SELECT ON [dbo].[topSecret] TO [myAppUserEscalated];
GO

----
IF OBJECT_ID(N'[dbo].[getSecrets]') IS NOT NULL
    DROP PROCEDURE [dbo].[getSecrets]
GO

CREATE PROCEDURE [dbo].[getSecrets]
AS
BEGIN
    SELECT SESSION_USER AS [User], ORIGINAL_LOGIN() AS [OriginalLogin];
    SELECT * FROM sys.login_token;
    SELECT * FROM sys.user_token;
    SELECT * FROM [dbo].[topSecret] AS [ts];
END
GO
GRANT EXECUTE ON [dbo].[getSecrets] TO [myAppUser];
GO
----
IF OBJECT_ID(N'[dbo].[getSecretsWithDynamicSql]') IS NOT NULL
    DROP PROCEDURE [dbo].[getSecretsWithDynamicSql]
GO

CREATE PROCEDURE [dbo].[getSecretsWithDynamicSql]
AS
BEGIN
    SELECT SESSION_USER AS [User], ORIGINAL_LOGIN() AS [OriginalLogin];
    SELECT * FROM sys.login_token;
    SELECT * FROM sys.user_token;
    EXEC(N'SELECT * FROM [dbo].[topSecret] AS [ts];');
END
GO
GRANT EXECUTE ON [dbo].[getSecretsWithDynamicSql] TO [myAppUser];
GO
----
IF OBJECT_ID(N'[dbo].[getSecretsWithDynamicSqlAndImpersonation]') IS NOT NULL
    DROP PROCEDURE [dbo].[getSecretsWithDynamicSqlAndImpersonation]
GO

CREATE PROCEDURE [dbo].[getSecretsWithDynamicSqlAndImpersonation]
WITH EXECUTE AS N'myAppUserEscalated'
AS
BEGIN
    SELECT SESSION_USER AS [User], ORIGINAL_LOGIN() AS [OriginalLogin];
    SELECT * FROM sys.login_token;
    SELECT * FROM sys.user_token;
    EXEC(N'SELECT * FROM [dbo].[topSecret] AS [ts];');
END
GO
GRANT EXECUTE ON [dbo].[getSecretsWithDynamicSqlAndImpersonation] TO [myAppUser];
GO
----
IF OBJECT_ID(N'[dbo].[getSecretsWithImpersonation]') IS NOT NULL
    DROP PROCEDURE [dbo].[getSecretsWithImpersonation]
GO

CREATE PROCEDURE [dbo].[getSecretsWithImpersonation]
WITH EXECUTE AS N'myAppUserEscalated'
AS
BEGIN
    SELECT SESSION_USER AS [User], ORIGINAL_LOGIN() AS [OriginalLogin];
    SELECT * FROM sys.login_token;
    SELECT * FROM sys.user_token;
    SELECT * FROM [dbo].[topSecret] AS [ts];
END
GO
GRANT EXECUTE ON [dbo].[getSecretsWithImpersonation] TO [myAppUser];
GO

/******************************

            DEMO

******************************/

/******************  TEST 1 (both DB_CHAINING and TRUSTWORTHY OFF) ********************/

-- Default is OFF, but make resetting after running Tests 2 and 3 easier
ALTER DATABASE [ChainingSource] SET DB_CHAINING OFF;
ALTER DATABASE [ChainingDestination] SET DB_CHAINING OFF;
ALTER DATABASE [ChainingSource] SET TRUSTWORTHY OFF;


USE [ChainingSource];

EXECUTE AS LOGIN = 'myAppUser';
SELECT SESSION_USER AS [User], ORIGINAL_LOGIN() AS [OriginalLogin];
GO

SELECT * FROM [dbo].[topSecret]; -- error:
-- Msg 229, Level 14, State 5, Line XXXXX
-- The SELECT permission was denied on the object 'topSecret',
--    database 'ChainingSource', schema 'dbo'.

EXEC [dbo].[getSecrets]; -- error:
-- Msg 229, Level 14, State 5, Procedure getSecrets, Line XXXXX
-- The SELECT permission was denied on the object 'topSecret',
--    database 'ChainingDestination', schema 'dbo'.

EXEC [dbo].[getSecretsWithImpersonation]; -- error:
-- Msg 916, Level 14, State 1, Procedure getSecretsWithImpersonation, Line XXXXX
-- The server principal "myAppUserEscalated" is not able to access the database
--    "ChainingDestination" under the current security context.


EXEC [dbo].[getSecretsWithDynamicSqlAndImpersonation]; -- error:
-- Msg 229, Level 14, State 5, Line XXXXX
-- The SELECT permission was denied on the object 'topSecret',
--    database 'ChainingSource', schema 'dbo'.

EXEC [dbo].[getSecretsWithDynamicSql]; -- error:
-- Msg 229, Level 14, State 5, Line XXXXX
-- The SELECT permission was denied on the object 'topSecret',
--    database 'ChainingSource', schema 'dbo'.

REVERT;
SELECT SESSION_USER AS [User], ORIGINAL_LOGIN() AS [OriginalLogin];
GO


/******************  TEST 2 (DB_CHAINING ON ; TRUSTWORTHY OFF) ************************/

ALTER DATABASE [ChainingSource] SET DB_CHAINING ON;
ALTER DATABASE [ChainingDestination] SET DB_CHAINING ON;
ALTER DATABASE [ChainingSource] SET TRUSTWORTHY OFF;
GO


EXECUTE AS LOGIN = 'myAppUser';
SELECT SESSION_USER AS [User], ORIGINAL_LOGIN() AS [OriginalLogin];
GO

SELECT * FROM [dbo].[topSecret]; -- error: same as in Test 1
EXEC [dbo].[getSecretsWithImpersonation]; -- error: same as in Test 1
EXEC [dbo].[getSecretsWithDynamicSql]; -- error: same as in Test 1


EXEC [dbo].[getSecrets]; -- (different) success!

EXEC [dbo].[getSecretsWithDynamicSqlAndImpersonation]; -- (different) error:
-- Msg 916, Level 14, State 1, Line XXXXX
-- The server principal "myAppUserEscalated" is not able to access the database
--    "ChainingDestination" under the current security context.

REVERT;
SELECT SESSION_USER AS [User], ORIGINAL_LOGIN() AS [OriginalLogin];
GO


/******************  TEST 3 (DB_CHAINING OFF ; TRUSTWORTHY ON) **********************/

ALTER DATABASE [ChainingSource] SET DB_CHAINING OFF;
ALTER DATABASE [ChainingDestination] SET DB_CHAINING OFF;
ALTER DATABASE [ChainingSource] SET TRUSTWORTHY ON;
GO


EXECUTE AS LOGIN = 'myAppUser';
SELECT SESSION_USER AS [User], ORIGINAL_LOGIN() AS [OriginalLogin];
GO

SELECT * FROM [dbo].[topSecret]; -- error: same as in Tests 1 and 2
EXEC [dbo].[getSecrets]; -- error: same as in Test 1
EXEC [dbo].[getSecretsWithDynamicSql]; -- error: same as in Tests 1 and 2


EXEC [dbo].[getSecretsWithImpersonation]; -- (different) success!

EXEC [dbo].[getSecretsWithDynamicSqlAndImpersonation]; -- (different) success:

REVERT;
SELECT SESSION_USER AS [User], ORIGINAL_LOGIN() AS [OriginalLogin];
GO


/******************  TEST 4 (both DB_CHAINING and TRUSTWORTHY ON) *********************/

ALTER DATABASE [ChainingSource] SET DB_CHAINING ON;
ALTER DATABASE [ChainingDestination] SET DB_CHAINING ON;
ALTER DATABASE [ChainingSource] SET TRUSTWORTHY ON;
GO


EXECUTE AS LOGIN = 'myAppUser';
SELECT SESSION_USER AS [User], ORIGINAL_LOGIN() AS [OriginalLogin];
GO

SELECT * FROM [dbo].[topSecret]; -- error: same as in Tests 1, 2, and 3
EXEC [dbo].[getSecretsWithDynamicSql]; -- error: same as in Tests 1, 2, and 3

EXEC [dbo].[getSecrets]; -- success: same as in Test 2

EXEC [dbo].[getSecretsWithImpersonation]; -- success: same as in Test 3

EXEC [dbo].[getSecretsWithDynamicSqlAndImpersonation]; -- success: same as in Test 3

REVERT;
SELECT SESSION_USER AS [User], ORIGINAL_LOGIN() AS [OriginalLogin];
GO





/*********************************************************************/
/* BEGIN: set up Certificate and cert-based Users for module signing */
/*********************************************************************/

USE [ChainingDestination];

CREATE MASTER KEY ENCRYPTION BY PASSWORD = N'f00bar!23';

CREATE CERTIFICATE [myAppCert]
  AUTHORIZATION [dbo]
  FROM BINARY = 0x\
308201BD30820126A003020102021061AF3EB269776BB74629F44629EF9216300D06092A864886F70D01\
01050500301C311A301806035504031311436F6465205369676E696E6720436572743020170D31363130\
32303232303932335A180F32303939303130313030303030305A301C311A301806035504031311436F64\
65205369676E696E67204365727430819F300D06092A864886F70D010101050003818D00308189028181\
00BCCA7DC1CF2F4874F341AF3C586F0B023CAAD16986ADDAC2F7BFB3BCE590F2A952218F51298067CE3B\
E9ED695A229DADD029510F0927F30484A587024E0F58EC83924BE49D227D2FE1FCCA0C682528D6A0658A\
AA6CA5D9F2405AB6950B7D5BA672BB971910D71CEE3B77FF0A4EF59F010AAB445FD127944966C141F7CF\
C3D5790203010001300D06092A864886F70D010105050003818100011525EDD191767041659701F13F43\
70F803E6C981A6E33D68863FDACADE709926AB7E3BC8D618EDD07FC52058EFE42D96CA49961CF2936F44\
6EDC4B7D55725FF2F1B37B326D564941CF6A7424551828FE198335AEFA0C892B375D3B676F35B708A48C\
67F80714643A34050CF9C557FBDD01274BC1ACCCA9A7AD3EC37DD2DA31;
-- no need for private key: nothing being signed in Destination


CREATE USER [myAppCert] FROM CERTIFICATE [myAppCert];

GRANT SELECT ON [dbo].[topSecret] TO [myAppCert];
GO


USE [ChainingSource];

CREATE MASTER KEY ENCRYPTION BY PASSWORD = N'f00bar!23';

CREATE CERTIFICATE [myAppCert]
  AUTHORIZATION [dbo]
  FROM BINARY = 0x\
308201BD30820126A003020102021061AF3EB269776BB74629F44629EF9216300D06092A864886F70D01\
01050500301C311A301806035504031311436F6465205369676E696E6720436572743020170D31363130\
32303232303932335A180F32303939303130313030303030305A301C311A301806035504031311436F64\
65205369676E696E67204365727430819F300D06092A864886F70D010101050003818D00308189028181\
00BCCA7DC1CF2F4874F341AF3C586F0B023CAAD16986ADDAC2F7BFB3BCE590F2A952218F51298067CE3B\
E9ED695A229DADD029510F0927F30484A587024E0F58EC83924BE49D227D2FE1FCCA0C682528D6A0658A\
AA6CA5D9F2405AB6950B7D5BA672BB971910D71CEE3B77FF0A4EF59F010AAB445FD127944966C141F7CF\
C3D5790203010001300D06092A864886F70D010105050003818100011525EDD191767041659701F13F43\
70F803E6C981A6E33D68863FDACADE709926AB7E3BC8D618EDD07FC52058EFE42D96CA49961CF2936F44\
6EDC4B7D55725FF2F1B37B326D564941CF6A7424551828FE198335AEFA0C892B375D3B676F35B708A48C\
67F80714643A34050CF9C557FBDD01274BC1ACCCA9A7AD3EC37DD2DA31
  WITH PRIVATE KEY (
     BINARY = 0x\
1EF1B5B00000000001000000010000001000000054020000CF21B85A9464B60CDAB9F2E419B341490702\
000000A4000002E67BDF3CE02406E4D69A760D519B3BB6DA77FAFA7D710936EAE5267F072F98A1F75211\
10EB03955427B79FA386F7D70EDF6E977E92E59761DE0EB186F895AD975CE63C4D8A8B67BA487B9807ED\
B8B33C7C08EABCCD716E9505170A9729B6E165CE0ED0CA35B5C62D548367FEFC2C694060184D91853314\
66A0C64A9CB7BF8CA7AC0946A54091A4626978320C7290A784C6147BAF23FD866D9D1D4D1D79DA1B4D2D\
E213D11299F1417D8C421CC25A2E851FF9CEFA0ECA2006186C787692FDC28F2E702FCC7E76AFBEB95B95\
4B50AA3697E60FC6928392664CE0EDB794AA392C1CC6326102B7CE8A02B367D2F416269DBBF4C16F0967\
80517D32B4653E94DE5C24CE9D39EBC8E6A4EF1B9217F1B4F098F4F77F88CC11C40DDB312BE87CC1430C\
16AED8773E7691ADE8472BFC02B458B09B40404F61D2E02746AE576582DEE3EC5C09077E127BB4E996A9\
C4A840E6E0F59D85A3FC4E2844679927DBA6A571927A1F1C938716B8FC922B1D77FAA90BDBA49D108408\
1E4198A50506C5F6FE87F81B759EE0688428ABA7B2E8CC7D96AC6409DAE41937DB9C1E1CACCD7AE86A8F\
161316A07B05D523A116AB87022978312EE9853AE9FFA44FFF52114D084934D86D0FFD2D47B974769812\
BF0F4FE8276FD0DCE4069F11EC3915A68F4454166E3ABAAB9539530117597EE52213FEC7C87254634F10\
062C5C1D97CE5FEABB13252B22E210F56DB281FC1CE5432A7144FB4B89D00B4F8BC876C8C0F397DB9D22\
E15E2B07FBB44ADDDFBB6A75728917AC330E3A9F978847AC2D27913B3B6CBF54F1BAEF06072D15050ED1\
CA7BF9C5763A,
  DECRYPTION BY PASSWORD = N'f00bar!23');

CREATE USER [myAppCert] FROM CERTIFICATE [myAppCert];

GRANT SELECT ON [dbo].[topSecret] TO [myAppCert];
GO

/*********************************************************************/
/* END: set up Certificate and cert-based Users for module signing */
/*********************************************************************/

-- Sign the two stored procedures that are NOT using Impersonation.
-- Ignore the two stored procedures that ARE using Impersonation.
ADD SIGNATURE TO [dbo].[getSecrets]
    BY CERTIFICATE [myAppCert];

ADD SIGNATURE TO [dbo].[getSecretsWithDynamicSql]
    BY CERTIFICATE [myAppCert];
GO


/******************  TEST 5 (both DB_CHAINING and TRUSTWORTHY OFF) ********************/

ALTER DATABASE [ChainingSource] SET DB_CHAINING OFF;
ALTER DATABASE [ChainingDestination] SET DB_CHAINING OFF;
-- Trustworthy? We don't need no stinkin' trustworthy ;-)
ALTER DATABASE [ChainingSource] SET TRUSTWORTHY OFF;
GO


EXECUTE AS LOGIN = N'myAppUser';
SELECT SESSION_USER AS [User], ORIGINAL_LOGIN() AS [OriginalLogin];
GO

SELECT * FROM [dbo].[topSecret]; -- error: same as in Tests 1, 2, 3, and 4

EXEC [dbo].[getSecrets]; -- SUCCESS!!!

EXEC [dbo].[getSecretsWithDynamicSql]; -- SUCCESS!!!
GO


REVERT;
SELECT SESSION_USER AS [User], ORIGINAL_LOGIN() AS [OriginalLogin];
GO


/************************  TEST 6 *************************************/
-- REMOVE Login/User: [myAppUserEscalated]
EXEC(N'
  USE [ChainingSource];
  IF (OBJECT_ID(N''[dbo].[getSecretsWithDynamicSqlAndImpersonation]'') IS NOT NULL)
  BEGIN
    DROP PROCEDURE [dbo].[getSecretsWithDynamicSqlAndImpersonation]
  END;
  IF (OBJECT_ID(N''[dbo].[getSecretsWithImpersonation]'') IS NOT NULL)
  BEGIN
    DROP PROCEDURE [dbo].[getSecretsWithImpersonation]
  END;
  IF (SUSER_ID(N''myAppUserEscalated'') IS NOT NULL)
  BEGIN
    DROP USER [myAppUserEscalated];
  END;


  USE [ChainingDestination];
  IF (SUSER_ID(N''myAppUserEscalated'') IS NOT NULL)
  BEGIN
    DROP USER [myAppUserEscalated];
  END;


  USE [master];
  IF (SUSER_ID(N''myAppUserEscalated'') IS NOT NULL)
  BEGIN
    DROP LOGIN [myAppUserEscalated];
  END;

');
GO

-- Now, re-run Test 5, just to be sure that it is only the module-signing that matters


--========================================

偽装とモジュール署名の比較

私が理解していない部分は、偽装で実行しているときにモジュール署名が機能しない理由です。 ...偽装はモジュール署名コンテキストを「拒否のみ」に変更しますか?

問題は、これらの質問が誤ってフレーム化されていることです。偽装に加えてモジュール署名を使用することは想定されていませんが、その代わりとして使用されます。これらは無料の機能ではありません。ここでの問題は、偽装がモジュールの署名にどのように影響するかではなく、一般的に偽装がどのように機能するかです。 (質問の)元のテストスクリプトの構造は、偽装とモジュール署名の関係についてのこの誤解に基づいています。これには、モジュールの署名が早すぎて、なりすましの動作自体を明確に見ることができないため、誤解を招く可能性があります。

上記のテストスクリプトを実行すると、なりすましが使用されたときにそれ自体(つまり、OFFDENYに設定されている-テスト1および2 )その後、サーバーレベルの「使用法」はDENY ONLYです。意味:データベースレベルの偽装を使用する場合、セキュリティコンテキストはデフォルトではであり、特定のデータベースに隔離されます。サーバーレベルに上がること、関連するログインのサーバーレベルの権限を取得すること、別のデータベースに戻ることはできません。

証明書、ログイン、ユーザーがまだ作成されていないため、これはモジュール署名とは関係ありません(例を順番にステップ実行していると想定しています)。そして、モジュール署名-追加権限を実行し、canデータベース間アクセスを許可-DENYをオーバーライドできません。 _権限は常にGRANT権限よりも優先されます。そのDENYTRUSTWORTHY ONによってのみ回避できます。

偽装を使用するときにサーバーレベルのDENY権限を削除できる唯一のことは、ソースデータベースのTRUSTWORTHYONに設定することです。テスト3と4は、TRUSTWORTHYが有効になるとthen偽装がデータベース間を行き来できることを示しています。繰り返しになりますが、これはテスト4まで設定されないため、モジュール署名とは関係ありません。モジュール署名は必要ではなく、全体的なシナリオを機能させます。必要なのは、なりすましだけですandTRUSTWORTHY ON。ただし、モジュールの署名が必要ですifTRUSTWORTHYを有効にしたくない場合、偽装の必要性を置き換えます。

次のグラフは、さまざまなシナリオとそれらに必要なものを示しています。

     Scenario         -->                 Requirements A               XOR   Requirements B
     ----------                ---------------------------------        |    --------------

Scope     Dynamic SQL --> DB_CHAINING    Impersonation   TRUSTWORTHY   XOR   Module Signing
Local      No               No               No              No         |        No
Local      YES              No               YES             No         |        YES

Cross-DB   No               YES              No              No         |        YES
Cross-DB   YES              No               YES             YES        |        YES

うまくいけば、モジュール署名がDB_CHAINING ONImpersonation、およびTRUSTWORTHY ONの必要性を完全に置き換えることができることは明らかです。クロスDB機能とダイナミックSQLを含むシナリオの両方を使用するシナリオを考えると、次の選択肢があります。

  1. DB_CHAINING ONTRUSTWORTHY ONの両方を設定します。

    これにより、動的SQLが使用されているために必要でない限り、not偽装を使用できます。したがって、一部のモジュールのみがEXECUTE AS句を取得します。

  2. TRUSTWORTHY ONのみを設定:

    allモジュールは偽装を使用する必要があります(つまり、EXECUTE AS句を使用します)。ただし、DB_CHAININGOFFに設定できます。

  3. モジュール署名のみを使用:

    これには、証明書とユーザーを両方のDBで作成し、ソースDBのすべてのCross-DBモジュールに署名する必要があります。ただし、DB_CHAININGandTRUSTWORTHYOFF !!に設定できます。また、ローカルの動的SQLであっても、偽装の必要はありません。このオプションは、すべてをよりクリーンかつ安全に処理します。


Microsoftからの確認

  • SQL Serverでのクロスデータベースアクセスの有効化

    動的SQL

    同じユーザーが両方のデータベースに存在しない限り、動的に作成されたSQLステートメントが実行される場合、データベース間の所有権の連鎖は機能しません。 SQL Serverでこれを回避するには、別のデータベースのデータにアクセスするストアドプロシージャを作成し、両方のデータベースに存在する証明書でプロシージャに署名します。これにより、ユーザーは、データベースへのアクセスや権限を付与せずに、手順で使用されるデータベースリソースにアクセスできます。

  • EXECUTE ASを使用したデータベース偽装の拡張

    なりすましの範囲について

    ...

    ただし、EXECUTE AS USERステートメントを使用してプリンシパルを偽装する場合、またはEXECUTE AS句を使用してデータベーススコープのモジュール内で使用する場合、偽装のスコープはデフォルトでデータベースに制限されます。つまり、データベースのスコープ外のオブジェクトを参照すると、エラーが返されます。

また、「EXECUTE ASを使用したデータベース偽装の拡張」MSDNページ(上記にリンク)には、認証システムとこれらのルールの背後にある理由を説明する多くの優れた情報があります。


詳細については、以下を参照してください。

6
Solomon Rutzky