SQL Serverの行ごとのアクセス


10

そのように構造化されたテーブルがあります(簡略化)

Name, EMail, LastLoggedInAt

SQL Server(RemoteUser)に、LastLoggdInAtフィールドがnullではないデータ(選択クエリ経由)のみを表示できるユーザーがいます。

できるように見える?出来ますか?


ここでは行レベルのセキュリティのためのBooks Onlineのトピックです:docs.microsoft.com/en-us/sql/relational-databases/security/...
デイビット・ブラウン-マイクロソフト

回答:


32

SQL Serverセキュリティモデルでは、基になるテーブルへのアクセスを許可せずに、ビューへのアクセスを許可できます。

コード例は概念を示すための優れた方法であるため、LoginDetailsテーブルとそれに対応するビューを使用して、以下を検討してください。

CREATE TABLE dbo.LoginDetails
(
    Username nvarchar(100) NOT NULL
    , EmailAddress nvarchar(256) NOT NULL
    , LastLoggedInAt datetime NULL
);
GO

CREATE VIEW dbo.LoginDetailsView
AS
SELECT ld.Username
    , ld.EmailAddress
    , ld.LastLoggedInAt
FROM dbo.LoginDetails ld
WHERE ld.LastLoggedInAt IS NOT NULL;
GO

ログインとユーザーを作成し、そのユーザーに、テーブル自体を表示する権限を持たずに、ビューから行を選択する権限を割り当てます。

CREATE LOGIN RemoteUser 
WITH PASSWORD = '2q1345lkjsadfgsa0(*';

CREATE USER RemoteUser
FOR LOGIN RemoteUser
WITH DEFAULT_SCHEMA = dbo;

GRANT SELECT ON dbo.LoginDetailsView TO RemoteUser;

次に、2つのテスト行を挿入します。

INSERT INTO dbo.LoginDetails(Username, EmailAddress, LastLoggedInAt)
VALUES ('user x', 'x@y.com', NULL)
    , ('user y', 'y@y.com', GETDATE());

これはセキュリティモデルをテストします。SELECTビューから選択しているため、最初のステートメントは成功しSELECTますが、ユーザーがテーブルに直接アクセスできないため、2番目のステートメントは失敗します。

EXECUTE AS LOGIN = 'RemoteUser';

SELECT *
FROM dbo.LoginDetailsView;
╔══════════╦══════════════╦═══════════════════════ ══╗
║ユーザー名║メールアドレス║LastLoggedInAt║
╠══════════╬══════════════╬═══════════════════════ ══╣
║ユーザーy║y@y.com║2018-02-15 07:36:54.490║
╚══════════╩══════════════╩═══════════════════════ ══╝
SELECT *
FROM dbo.LoginDetails;

REVERT

質問での必要に応じて、ビューの結果から、LastLoggedInAt値がNULLである行が除外されていることに注意してください。

SELECT基になるテーブルに対する2番目のステートメントはエラーを返します。

メッセージ229、レベル14、状態5、行28
SELECT権限がオブジェクト 'LoginDetails'、データベース 'tempdb'、スキーマ 'dbo'で拒否されました。

掃除:

DROP USER RemoteUser;
DROP LOGIN RemoteUser;
DROP VIEW dbo.LoginDetailsView;
DROP TABLE dbo.LoginDetails;

または、SQL Server 2016以降を使用している場合は、行レベルのセキュリティ述語を使用して、特定のユーザーにNULL LastLoggedInAt値の行が表示されないようにすることができます。

まず、テーブル、ログイン、そのログインのユーザーを作成し、テーブルへのアクセスを許可します。

CREATE TABLE dbo.LoginDetails
(
    Username nvarchar(100) NOT NULL
    , EmailAddress nvarchar(256) NOT NULL
    , LastLoggedInAt datetime NULL
);
GO

CREATE LOGIN RemoteUser 
WITH PASSWORD = '2q1345lkjsadfgsa0(*';

CREATE USER RemoteUser
FOR LOGIN RemoteUser
WITH DEFAULT_SCHEMA = dbo;

GRANT SELECT ON dbo.LoginDetails TO RemoteUser;

次に、いくつかのサンプル行を挿入します。nullを持つ1つの行と、その列にnull LastLoggedInAt以外の値を持つ1つの行。

INSERT INTO dbo.LoginDetails(Username, EmailAddress, LastLoggedInAt)
VALUES ('user x', 'x@y.com', NULL)
    , ('user y', 'y@y.com', GETDATE());

ここでは、関数に渡された変数@LastLoggedInAt@username変数の値に応じて0または1の行を返すスキーマバインドテーブル値関数を作成しています。この関数は、特定のユーザーに対して非表示にする行を除外するために、フィルター述語によって使用されます。

CREATE FUNCTION dbo.fn_LoginDetailsRemoteUserPredicate
(
    @LastLoggedInAt datetime
    , @username sysname
)  
RETURNS TABLE  
WITH SCHEMABINDING  
AS  
    RETURN SELECT 1 AS fn_securitypredicate_result   
    WHERE (@username = N'RemoteUser' AND @LastLoggedInAt IS NOT NULL)
        OR @username <> N'RemoteUser';  
GO

これは、テーブルSELECTに対して実行されたステートメントから行を削除するセキュリティフィルターですdbo.LoginDetails

CREATE SECURITY POLICY LoginDetailsRemoteUserPolicy
ADD FILTER PREDICATE dbo.fn_LoginDetailsRemoteUserPredicate(LastLoggedInAt, USER_NAME())
ON dbo.LoginDetails
WITH (STATE=ON);

上記のフィルターdbo.fn_LoginDetailsRemoteUserPredicateは、現在のユーザーの名前とLastLoggedInAtdbo.LoginDetailsテーブルの列の各行の値を渡すことにより、関数を使用します。

通常のユーザーとしてテーブルをクエリすると:

SELECT *
FROM dbo.LoginDetails

すべての行が表示されます。

╔══════════╦══════════════╦═══════════════════════ ══╗
║ユーザー名║メールアドレス║LastLoggedInAt║
╠══════════╬══════════════╬═══════════════════════ ══╣
║ユーザーx║x@y.com║NULL║
║ユーザーy║y@y.com║2018-02-15 13:53:42.577║
╚══════════╩══════════════╩═══════════════════════ ══╝

ただし、次のようにテストした場合RemoteUser

EXECUTE AS LOGIN = 'RemoteUser';

SELECT *
FROM dbo.LoginDetails

REVERT

「有効な」行のみが表示されます。

╔══════════╦══════════════╦═══════════════════════ ══╗
║ユーザー名║メールアドレス║LastLoggedInAt║
╠══════════╬══════════════╬═══════════════════════ ══╣
║ユーザーy║y@y.com║2018-02-15 13:42:02.023║
╚══════════╩══════════════╩═══════════════════════ ══╝

そして、私たちはクリーンアップします:

DROP SECURITY POLICY LoginDetailsRemoteUserPolicy;
DROP FUNCTION dbo.fn_LoginDetailsRemoteUserPredicate;
DROP USER RemoteUser;
DROP LOGIN RemoteUser;
DROP TABLE dbo.LoginDetails;

この方法で関数をテーブルにスキーマバインドすると、最初にフィルター述語とdbo.fn_LoginDetailsRemoteUserPredicate関数を削除せずにテーブルの定義を変更できなくなることに注意してください。


素晴らしい答え-ありがとう!これら2つの方法のパフォーマンスへの影響はどうですか。この関数を使用すると、Webアプリの速度が最大5倍遅くなることがわかりました。Viewメソッドを確認する必要があります。
LiamB 2018年

行レベルのセキュリティ関数は、テーブルから読み取られたすべての行に対して評価されます。そのテーブルへのアクセスが大幅に遅くなると思います。一方、ビューは、LastLoggedInAt列に有用なインデックスを作成した場合、パフォーマンスへの影響はごくわずかです。
Max Vernon

それは理にかなっています-私は今ビューを見るでしょう、うまく機能しているようです!ユーザーが、条件に一致するこれらの行のユーザーデータのみを編集できるようにしたい場合、ビューでそれが可能でしょうか?
LiamB 2018年

美しい、すべての作業-これで助けてくれてありがとう
LiamB

はい、ビューから行を編集できます
Max Vernon
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.