ฉันมีโครงสร้างตารางดังนั้น (แบบง่าย)
Name, EMail, LastLoggedInAt
ฉันมีผู้ใช้ใน SQL Server (RemoteUser) ที่ควรจะสามารถดูข้อมูลได้ (ผ่านแบบสอบถามแบบใช้เลือกข้อมูล) ที่ฟิลด์ LastLoggdInAt ไม่ว่าง
ดูเหมือนว่าฉันจะทำเช่นนี้? เป็นไปได้ไหม?
ฉันมีโครงสร้างตารางดังนั้น (แบบง่าย)
Name, EMail, LastLoggedInAt
ฉันมีผู้ใช้ใน SQL Server (RemoteUser) ที่ควรจะสามารถดูข้อมูลได้ (ผ่านแบบสอบถามแบบใช้เลือกข้อมูล) ที่ฟิลด์ LastLoggdInAt ไม่ว่าง
ดูเหมือนว่าฉันจะทำเช่นนี้? เป็นไปได้ไหม?
คำตอบ:
โมเดลความปลอดภัยของ 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;
ตอนนี้เราจะแทรกแถวทดสอบสองแถว:
INSERT INTO dbo.LoginDetails(Username, EmailAddress, LastLoggedInAt)
VALUES ('user x', 'x@y.com', NULL)
, ('user y', 'y@y.com', GETDATE());
นี่เป็นการทดสอบโมเดลความปลอดภัย SELECT
คำสั่งแรกประสบความสำเร็จเนื่องจากมันถูกเลือกจากมุมมองในขณะที่SELECT
คำสั่งที่สองล้มเหลวเนื่องจากผู้ใช้ไม่สามารถเข้าถึงตารางโดยตรง
EXECUTE AS LOGIN = 'RemoteUser';
SELECT *
FROM dbo.LoginDetailsView;
╔══════════╦══════════════╦═══════════════════════ ══╗ ║ชื่อผู้ใช้║ที่อยู่อีเมล║ LastLoggedInAt ║ ╠══════════╬══════════════╬═══════════════════════ ══╣ ║ user y ║ y@y.com ║ 2018-02-15 07: 36: 54.490 ║ ╚══════════╩══════════════╩═══════════════════════ ══╝
SELECT *
FROM dbo.LoginDetails;
REVERT
สังเกตผลลัพธ์จากมุมมองไม่รวมแถวที่มีLastLoggedInAt
ค่าNULL
ตามที่ต้องการในคำถามของคุณ
SELECT
คำสั่งที่สองเทียบกับตารางที่อยู่เบื้องหลังส่งคืนข้อผิดพลาด:
เกี่ยวกับข่าวสาร 229 ระดับ 14 สถานะ 5 บรรทัด 28
สิทธิ์ SELECT ถูกปฏิเสธบนวัตถุ 'LoginDetails' ฐานข้อมูล 'tempdb', schema 'dbo'
ทำความสะอาด:
DROP USER RemoteUser;
DROP LOGIN RemoteUser;
DROP VIEW dbo.LoginDetailsView;
DROP TABLE dbo.LoginDetails;
อีกวิธีหนึ่งคือถ้าคุณมี SQL Server 2016 หรือใหม่กว่า, คุณสามารถใช้ระดับแถวรักษาความปลอดภัยกริยาเพื่อป้องกันไม่ให้ผู้ใช้บางรายเห็นแถวที่มีโมฆะ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 LastLoggedInAt
และหนึ่งแถวที่มีค่าไม่เป็นศูนย์สำหรับคอลัมน์นั้น
INSERT INTO dbo.LoginDetails(Username, EmailAddress, LastLoggedInAt)
VALUES ('user x', 'x@y.com', NULL)
, ('user y', 'y@y.com', GETDATE());
ที่นี่เรากำลังสร้างฟังก์ชั่นที่มีมูลค่าเป็นตารางที่มีค่าเป็นสคีมาซึ่งส่งคืนแถวที่มี 0 หรือ 1 ขึ้นอยู่กับค่าของ@LastLoggedInAt
และ@username
ตัวแปรที่ส่งผ่านเข้าไปในฟังก์ชัน ฟังก์ชั่นนี้จะถูกใช้โดยตัวกรองเพรดิเคตเพื่อกำจัดแถวที่เราต้องการซ่อนจากผู้ใช้บางคน
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
ฟังก์ชันโดยส่งผ่านชื่อผู้ใช้ปัจจุบันพร้อมกับค่าจากแต่ละแถวสำหรับLastLoggedInAt
คอลัมน์จากdbo.LoginDetails
ตาราง
หากเราสอบถามตารางในฐานะผู้ใช้ปกติ:
SELECT *
FROM dbo.LoginDetails
เราเห็นทุกแถว:
╔══════════╦══════════════╦═══════════════════════ ══╗ ║ชื่อผู้ใช้║ที่อยู่อีเมล║ LastLoggedInAt ║ ╠══════════╬══════════════╬═══════════════════════ ══╣ ║ user x ║ x@y.com ║ NULL ║ ║ user y ║ y@y.com ║ 2018-02-15 13: 53: 42.577 ║ ╚══════════╩══════════════╩═══════════════════════ ══╝
อย่างไรก็ตามหากเราทดสอบเป็นRemoteUser
:
EXECUTE AS LOGIN = 'RemoteUser';
SELECT *
FROM dbo.LoginDetails
REVERT
เราเห็นเฉพาะแถวที่ "ถูกต้อง":
╔══════════╦══════════════╦═══════════════════════ ══╗ ║ชื่อผู้ใช้║ที่อยู่อีเมล║ LastLoggedInAt ║ ╠══════════╬══════════════╬═══════════════════════ ══╣ ║ user 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
ฟังก์ชันก่อน
LastLoggedInAt
คอลัมน์