SQL Server Baris demi Baris Akses

10

Saya memiliki tabel terstruktur seperti itu (Sederhana)

Name, EMail, LastLoggedInAt

Saya memiliki pengguna di SQL Server (RemoteUser) yang seharusnya hanya dapat melihat data (Melalui kueri pemilihan) di mana bidang LastLoggdInAt bukan nol.

Sepertinya saya bisa melakukan ini? Apa itu mungkin?

LiamB
sumber
Inilah topik Books Online untuk Row-Level Security: docs.microsoft.com/en-us/sql/relational-databases/security/…
David Browne - Microsoft

Jawaban:

32

Model keamanan SQL Server memungkinkan Anda untuk memberikan akses ke tampilan tanpa memberikan akses ke tabel yang mendasarinya.

Karena kode contoh adalah cara yang bagus untuk menunjukkan konsep, pertimbangkan yang berikut, dengan LoginDetailstabel, dan tampilan yang sesuai:

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

Kami akan membuat login, dan pengguna, lalu memberikan hak kepada pengguna itu untuk memilih baris dari tampilan, tanpa memiliki hak untuk melihat tabel itu sendiri.

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

CREATE USER RemoteUser
FOR LOGIN RemoteUser
WITH DEFAULT_SCHEMA = dbo;

GRANT SELECT ON dbo.LoginDetailsView TO RemoteUser;

Sekarang, kami akan memasukkan dua baris uji:

INSERT INTO dbo.LoginDetails(Username, EmailAddress, LastLoggedInAt)
VALUES ('user x', '[email protected]', NULL)
    , ('user y', '[email protected]', GETDATE());

Ini menguji model keamanan. SELECTPernyataan pertama berhasil, karena memilih dari tampilan, sedangkan SELECTpernyataan kedua gagal karena pengguna tidak memiliki akses langsung ke tabel.

EXECUTE AS LOGIN = 'RemoteUser';

SELECT *
FROM dbo.LoginDetailsView;
╔══════════╦══════════════╦═══════════════════════ ══╗
║ Nama Pengguna ║ Alamat Email ║ LastLoggedInAt ║
╠══════════╬══════════════╬═══════════════════════ ══╣
║ pengguna y ║ [email protected] ║ 2018-02-15 07: 36: 54.490 ║
╚══════════╩══════════════╩═══════════════════════ ══╝
SELECT *
FROM dbo.LoginDetails;

REVERT

Catat hasil dari tampilan mengecualikan baris tempat LastLoggedInAtnilainya NULL, seperti yang dipersyaratkan dalam pertanyaan Anda.

SELECTPernyataan kedua terhadap tabel yang mendasari menghasilkan kesalahan:

Msg 229, Level 14, Negara 5, Jalur 28
Izin SELECT ditolak pada objek 'LoginDetails', database 'tempdb', schema 'dbo'.

Membersihkan:

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

Sebagai alternatif, jika Anda memiliki SQL Server 2016 atau yang lebih baru, Anda bisa menggunakan predikat tingkat-keamanan untuk mencegah pengguna tertentu melihat baris dengan LastLoggedInAtnilai NULL .

Pertama, kami membuat tabel, login, pengguna untuk login itu, dan kami memberikan akses ke tabel:

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;

Selanjutnya, kami menyisipkan beberapa baris sampel. Satu baris dengan nol LastLoggedInAt, dan satu dengan nilai bukan nol untuk kolom itu.

INSERT INTO dbo.LoginDetails(Username, EmailAddress, LastLoggedInAt)
VALUES ('user x', '[email protected]', NULL)
    , ('user y', '[email protected]', GETDATE());

Di sini, kami membuat fungsi tabel-nilai-terikat-skema-terikat yang mengembalikan baris dengan 0 atau 1 tergantung pada nilai @LastLoggedInAtdan @usernamevariabel yang diteruskan ke fungsi. Fungsi ini akan digunakan oleh predikat-filter untuk menghilangkan baris yang ingin kita sembunyikan dari pengguna tertentu.

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

Ini adalah filter keamanan yang menghilangkan baris dari SELECTpernyataan yang dijalankan terhadap dbo.LoginDetailstabel:

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

Filter di atas menggunakan dbo.fn_LoginDetailsRemoteUserPredicatefungsi dengan mengirimkan nama pengguna saat ini, bersama dengan nilai dari setiap baris untuk LastLoggedInAtkolom dari dbo.LoginDetailstabel.

Jika kami meminta tabel sebagai pengguna normal:

SELECT *
FROM dbo.LoginDetails

kita melihat semua baris:

╔══════════╦══════════════╦═══════════════════════ ══╗
║ Nama Pengguna ║ Alamat Email ║ LastLoggedInAt ║
╠══════════╬══════════════╬═══════════════════════ ══╣
║ pengguna x ║ [email protected] ║ NULL ║
║ pengguna y ║ [email protected] ║ 2018-02-15 13: 53: 42.577 ║
╚══════════╩══════════════╩═══════════════════════ ══╝

Namun, jika kami uji sebagai RemoteUser:

EXECUTE AS LOGIN = 'RemoteUser';

SELECT *
FROM dbo.LoginDetails

REVERT

kami hanya melihat baris "valid":

╔══════════╦══════════════╦═══════════════════════ ══╗
║ Nama Pengguna ║ Alamat Email ║ LastLoggedInAt ║
╠══════════╬══════════════╬═══════════════════════ ══╣
║ pengguna y ║ [email protected] ║ 2018-02-15 13: 42: 02.023 ║
╚══════════╩══════════════╩═══════════════════════ ══╝

Dan, kami membersihkan:

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

Perlu diketahui bahwa pengikatan skema fungsi ke tabel dengan cara ini tidak membuat tidak mungkin untuk mengubah definisi tabel tanpa terlebih dahulu menjatuhkan predikat filter, dan dbo.fn_LoginDetailsRemoteUserPredicatefungsi.

Max Vernon
sumber
Jawaban yang brilian - terima kasih! Apa implikasi kinerja 2 metode ini. Kami telah menemukan bahwa aplikasi web kami hingga 5 kali lebih lambat ketika kami menggunakan fungsi ini. Perlu melihat Metode Tampilan.
LiamB
Fungsi keamanan tingkat baris dievaluasi untuk setiap baris yang dibaca dari tabel; Saya berharap ini akan secara signifikan memperlambat akses ke tabel itu. Pandangan, di sisi lain, harus memiliki dampak yang dapat diabaikan pada kinerja dengan asumsi Anda membuat indeks yang berguna pada LastLoggedInAtkolom.
Max Vernon
Itu masuk akal - saya akan melihat pemandangan sekarang, tampaknya bekerja dengan baik! Jika kami ingin pengguna hanya dapat mengedit data pengguna untuk baris ini yang cocok dengan kriteria apakah itu mungkin dengan tampilan?
LiamB
Cantik, semuanya bekerja - terima kasih atas bantuannya
LiamB
Ya, Anda dapat mengedit baris melalui tampilan
Max Vernon