動的SQLを使用するストアドプロシージャの実行権限をユーザーに付与しました。しかし、それを実行しようとすると、エラーが発生します。
オブジェクト '[テーブル名]'、データベース '[データベース名]'、スキーマ 'dbo'に対するSELECT権限が拒否されました。
ストアドプロシージャが使用するテーブルに対するアクセス許可をユーザーに付与する必要がありますか?それは私には本当に意味がありません。
さて、上記のコメントに基づいて、そして私の疑惑に従って-あなたはあなたのストアドプロシージャ内で動的SQLを実行しようとしているようです。
覚えておく必要があるのは、これを実行しても、ストアドプロシージャのコンテキスト内では実行されず、新しいセッション内で実行されるということです。このため、ステートメントがストアドプロシージャ内で呼び出されているという事実は重要なポイントであり、動的SQLが使用しているオブジェクトに対して明示的な権限を付与する必要があります。
これを実行したくない場合は、動的SQLを使用しないようにストアドプロシージャをリファクタリングします。
マイクロソフトからの以下のリンクは、問題の解決に役立ちます。
PRB:ストアドプロシージャ内の動的SQLステートメントのセキュリティコンテキスト(ウェイバックマシンアーカイブ)
この現象は、動的実行クエリ(sp_executesqlまたはEXECUTE)がメインストアドプロシージャとは別のコンテキストで実行されるために発生します。ストアドプロシージャの所有者のセキュリティコンテキストではなく、ストアドプロシージャを実行するユーザーのセキュリティコンテキストで実行されます。
これについては、(最新の)Microsoft Docsの記事でも説明されています。
SQL ServerでのセキュアダイナミックSQLの書き込み
プロシージャコードで動的に作成されたSQLステートメントを実行すると、所有権の連鎖が壊れ、SQL Serverは動的SQLによってアクセスされているオブジェクトに対して呼び出し元の権限をチェックします。
プロシージャの所有者と、SELECTがクエリしている基になるオブジェクトが異なるようです。これはすべて Ownership Chains に関係しています。私が話していることの簡単な説明とデモンストレーションについては、以下の例を参照してください。
use YourTestDatabase;
go
create login TestLogin1
with
password = 'password',
check_policy = off;
go
create user TestUser1
for login TestLogin1;
go
create table Table1
(
id int identity(1, 1) not null,
SomeString varchar(30) not null
default replicate('a', 30)
);
go
insert into Table1
values(default);
go 10
create proc Proc1
as
select *
from Table1;
go
grant execute
on Proc1
to TestUser1;
go
-- this works because permissions aren't checked
-- on Table1. That is why TestUser1 can get the
-- result set without SELECT permissions on Table1
execute as user = 'TestUser1';
go
exec Proc1;
go
revert;
go
-- let's change the owner of Proc1 so that the
-- ownership chain is broken and permissions are
-- checked on Table1
alter authorization
on Proc1
to TestUser1;
go
-- this no longer works because permissions are now
-- checked on Table1, which TestUser1 does not have
-- SELECT permissions on
execute as user = 'TestUser1';
go
exec Proc1;
go
revert;
go
オブジェクトの所有権を確認したい場合は、以下のクエリを実行できます(明らかに、WHERE句を変更して特定のオブジェクト名を含めるようにします)。
select
o.name,
o.type_desc,
case
when o.principal_id is null
then sp.name
else dp.name
end as principal_name
from sys.objects o
inner join sys.schemas s
on o.schema_id = s.schema_id
left join sys.database_principals dp
on o.principal_id = dp.principal_id
left join sys.database_principals sp
on s.principal_id = sp.principal_id
where o.name in
(
'Table1',
'Proc1'
);