現在のSQL Server 2012データベースに、db_datareader
、db_datawriter
、およびexecute
権限を持つロールを作成しました。現在のdbのプロシージャは、msdbのsp_start_job
を呼び出します。
Msdbでプロシージャを実行するためにデータベースロールへのアクセスを許可するにはどうすればよいですか?
sp_start_job
を所有者として呼び出す現在のデータベースでプロシージャを実行しようとしましたが、ユーザー定義のロールのメンバーであるユーザーはまだプロシージャを実行できません。
ログイン用のサーバーロールpublic
を作成し、それをmsdbデータベースにマップしましたが、アクセス許可を付与できません。私が実行しようとしているコマンドは次のとおりです。
GRANT EXECUTE ON OBJECT::[msdb].[dbo].[sp_start_job] TO [db_executor]
Msdbから実行すると、次のエラーが発生します。
ユーザー 'db_executor'が存在しないか、権限がないため、見つかりません。
ユーザー定義ロールdb_executor
が作成されたデータベースから実行すると、次のエラーがスローされます。
現在のデータベース内のオブジェクトに対する権限のみを付与または取り消すことができます。
ユーザーをSQLAgentOperatorRole
に追加したくありません。アイデアは、アプリのデータベース内のすべてのユーザーを1つのロールにグループ化することであり、このロールのメンバーは、sp_start_job
を呼び出すデータベース内のプロシージャを実行できる必要があります。
これは、アプリケーションにSQLエージェントジョブをほぼ完全に制御させることなく実行できます。 msdb
にユーザーを作成する代わりに、現在のDBとmsdb
の両方に証明書を作成し、証明書に関連するいくつかの追加手順を作成します。これを行う手順は、以下の作業例に示されています。
初期設定とテスト1:
USE [master];
---
CREATE DATABASE [SecureJobStarter] COLLATE Latin1_General_100_CI_AS_SC;
CREATE LOGIN [CannotStartJob]
WITH PASSWORD = N'YouCallThisAPassword?',
DEFAULT_DATABASE = [master],
CHECK_EXPIRATION = OFF,
CHECK_POLICY = OFF;
GO
---
USE [SecureJobStarter];
GO
CREATE PROCEDURE [dbo].[StartJob]
(
@JobName sysname
)
AS
SET NOCOUNT ON;
EXEC [msdb].[dbo].[sp_start_job] @job_name = @JobName;
GO
---
CREATE ROLE [JobStarter];
GRANT EXECUTE ON [dbo].[StartJob] TO [JobStarter];
CREATE USER [CannotStartJob] FOR LOGIN [CannotStartJob];
ALTER ROLE [JobStarter] ADD MEMBER [CannotStartJob];
GO
----------------------
-- TEST 1
USE [SecureJobStarter];
EXECUTE AS LOGIN = N'CannotStartJob';
SELECT SESSION_USER AS [User], ORIGINAL_LOGIN() AS [OriginalLogin];
EXEC(N'USE [msdb]; EXEC msdb.dbo.sp_start_job @job_name = N''StartJobTest'';');
/*
Msg 229, Level 14, State 5, Procedure sp_start_job, Line xxxxx
The EXECUTE permission was denied on the object 'sp_start_job',
database 'msdb', schema 'dbo'.
*/
EXECUTE [dbo].[StartJob] N'StartJobTest';
/*
Msg 229, Level 14, State 5, Procedure sp_start_job, Line xxxxx
The EXECUTE permission was denied on the object 'sp_start_job',
database 'msdb', schema 'dbo'.
*/
REVERT;
SELECT SESSION_USER AS [User], ORIGINAL_LOGIN() AS [OriginalLogin];
GO
----------------------
ご覧のとおり、現在sp_start_job
を[msdb]
で直接実行することはできません(ログインは少なくともEnterでアクセスできます)、またはローカルストアドプロシージャを介して実行することはできません。
証明書と証明書ベースのユーザーを作成し、テスト2:
USE [SecureJobStarter];
DECLARE @SQL2 NVARCHAR(MAX);
SET @SQL2 = N'
-- Create in current DB...
CREATE CERTIFICATE [Permission:AgentOperator$Cert]
ENCRYPTION BY PASSWORD = N''MyCertificate!MineMineMine!''
WITH SUBJECT = N''Grant the SQLAgentOperatorRole Role'',
EXPIRY_DATE = ''2099-12-31'';
DECLARE @CertificateBytes VARBINARY(MAX),
@PrivateKeyBytes VARBINARY(MAX),
@CertID INT;
SET @CertID = CERT_ID(N''Permission:AgentOperator$Cert'');
SELECT @CertificateBytes = CERTENCODED(@CertID),
@PrivateKeyBytes = CERTPRIVATEKEY(@CertID,
N''MyCertificate!MineMineMine!'', N''MyCertificate!MineMineMine!'');
-- Now recreate same Cert in [msdb]...
USE [msdb];
DECLARE @SQL3 NVARCHAR(MAX);
SET @SQL3 = N''
--------------------------------
CREATE CERTIFICATE [Permission:AgentOperator$Cert]
FROM BINARY = '' + CONVERT(NVARCHAR(MAX), @CertificateBytes, 1) + N''
WITH PRIVATE KEY
(
BINARY = '' + CONVERT(NVARCHAR(MAX), @PrivateKeyBytes, 1) + N'',
DECRYPTION BY PASSWORD = N''''MyCertificate!MineMineMine!'''',
ENCRYPTION BY PASSWORD = N''''MyCertificate!MineMineMine!''''
);
--------------------------------
'';
EXEC(@SQL3);
CREATE USER [Permission:AgentOperator$User]
FROM CERTIFICATE [Permission:AgentOperator$Cert];
ALTER ROLE [SQLAgentOperatorRole]
ADD MEMBER [Permission:AgentOperator$User];
';
EXEC(@SQL2);
----
-- Finally, link the stored procedure in the current DB with the Certificate-based
-- User in [msdb] by signing the stored procedure...
ADD SIGNATURE
TO [dbo].[StartJob]
BY CERTIFICATE [Permission:AgentOperator$Cert]
WITH PASSWORD = N'MyCertificate!MineMineMine!';
GO
----------------------
-- TEST 2
USE [SecureJobStarter];
EXECUTE AS LOGIN = N'CannotStartJob';
SELECT SESSION_USER AS [User], ORIGINAL_LOGIN() AS [OriginalLogin];
EXEC(N'USE [msdb]; EXEC msdb.dbo.sp_start_job @job_name = N''StartJobTest'';');
/*
Msg 229, Level 14, State 5, Procedure sp_start_job, Line xxxxx
The EXECUTE permission was denied on the object 'sp_start_job',
database 'msdb', schema 'dbo'.
*/
EXECUTE [dbo].[StartJob] N'StartJobTest';
/*
Msg 14262, Level 16, State 1, Procedure sp_verify_job_identifiers, Line xxxxx
The specified @job_name ('StartJobTest') does not exist.
*/
REVERT;
SELECT SESSION_USER AS [User], ORIGINAL_LOGIN() AS [OriginalLogin];
GO
----------------------
上記を見るとわかるように、sp_start_job
を直接実行する機能はまだありませんが、今回はローカルストアドプロシージャがさらに長くなりました。sp_start_job
を実行できましたが、サブでエラーが発生しました-手続き呼び出し– sp_verify_job_identifiers
– sp_start_job
内。
[Counter]署名を追加してテスト3:
私はもともとこれを副署名として設定しましたが、それ以来、この場合、通常の署名と副署名の間に実質的な違いはなく、それらの間には一般的に意味のある/有益な違いがないことがわかりました)。ここでカウンター署名を使用できますが、必要ないようです。プロセスの開始が署名されたプロシージャdbo.StartJob
であることを確認するだけであり、[msdb]
のprocでプロセスを開始できません。ただし、このモジュール署名を行う目的全体がユーザーに[msdb]
へのアクセス権が与えられることは決してないということを考えると、とにかくそのような状況の可能性はありませんでした。
USE [SecureJobStarter];
EXEC(N'
USE [msdb];
ADD COUNTER SIGNATURE -- "COUNTER" keyword to the left should be optional
TO [dbo].[sp_start_job]
BY CERTIFICATE [Permission:AgentOperator$Cert]
WITH PASSWORD = N''MyCertificate!MineMineMine!'';
ADD COUNTER SIGNATURE -- "COUNTER" keyword to the left should be optional
TO [dbo].[sp_verify_job_identifiers]
BY CERTIFICATE [Permission:AgentOperator$Cert]
WITH PASSWORD = N''MyCertificate!MineMineMine!'';
ADD COUNTER SIGNATURE -- "COUNTER" keyword to the left should be optional
TO [dbo].[sp_sqlagent_notify]
BY CERTIFICATE [Permission:AgentOperator$Cert]
WITH PASSWORD = N''MyCertificate!MineMineMine!'';
');
GO
----------------------
-- TEST 3
USE [SecureJobStarter];
EXECUTE AS LOGIN = N'CannotStartJob';
SELECT SESSION_USER AS [User], ORIGINAL_LOGIN() AS [OriginalLogin];
EXEC(N'USE [msdb]; EXEC msdb.dbo.sp_start_job @job_name = N''StartJobTest'';');
/*
Msg 229, Level 14, State 5, Procedure sp_start_job, Line xxxxx
The EXECUTE permission was denied on the object 'sp_start_job',
database 'msdb', schema 'dbo'.
*/
EXECUTE [dbo].[StartJob] N'StartJobTest'; -- SUCCESS!!!
/*
"Messages" tab: Job 'StartJobTest' started successfully.
Job History : The job succeeded. The Job was invoked by User CannotStartJob.
*/
REVERT;
SELECT SESSION_USER AS [User], ORIGINAL_LOGIN() AS [OriginalLogin];
GO
----------------------
上記のように、sp_start_job
を直接実行する機能はまだありませんが、今回はローカルストアドプロシージャが実際に成功しました:-) !!
したがって、このアプローチは確かに、アプリをユーザーとして[msdb]
に追加し、そのユーザーをロールに追加するために必要な2行ほど簡単ではありませんが、セキュリティリスクを示さないという明確な利点があります。証明書ベースのユーザーを偽装することはできないため、sp_start_job
を実行する権限は、実際にはこのローカルストアドプロシージャに完全に限定されています。意味:アプリケーションにユーザーを作成してmsdb
へのログインアクセスを許可し、そのユーザーをSQLAgentOperatorRole
データベースロールに追加すると、そのログインを開始できるだけでなくanyジョブ、ただしSQLAgentOperatorRole
Roleが許可するanythingを実行できます。
(恐怖、悲鳴、泣き声)。どうして?開発者(および潜在的にハッカー)は、(SQLエージェントジョブに関して)アプリのコードに何でもさせ、それについて尋ねる必要がないため、それについて教えてください(誰かがチケットを提出して、そのような機能が期待どおりに機能していないこと、そして6か月から12か月に導入されたその機能について最初に聞いたことが、最終的にわかると思いますこのサポートリクエストを受け取る前に、;-)。
対照的に、モジュール署名アプローチ(つまり、証明書とADD [COUNTER] SIGNATURE
を使用)は非常に安全です。ストアドプロシージャで単一のジョブ名をハードコーディングして、アプリケーションがその1つのジョブしか開始できないようにすることができます。または、ストアード・プロシージャーにTINYINT
/INT
パラメーターを受け入れさせ、CASE
またはIF
を使用してマップすることにより、いくつかの特定のジョブのみを開始できるようにすることができます。値1
、2
、3
、...をジョブ名に:
DECLARE @JobName sysname;
SET @JobName = CASE @JobNumber -- input param
WHEN 1 THEN N'this_job'
WHEN 2 THEN N'that_job'
WHEN 3 THEN N'what_job'
END;
EXEC [msdb].[dbo].[sp_start_job] @job_name = @JobName;
誰かがこっそりとこのストアドプロシージャを更新して、SQLAgentOperatorRole
ロールを利用する別のことを実行しようとすると、署名が削除され、msdb
とその機能でエラーが発生し始めます。これは調査のために返されます。つまり、変更を確認でき、承認しない場合はADD SIGNATURE
を再度実行する必要はありません。
詳細については、次のリソースを参照してください。