SQL Server Row โดยการเข้าถึงแถว


10

ฉันมีโครงสร้างตารางดังนั้น (แบบง่าย)

Name, EMail, LastLoggedInAt

ฉันมีผู้ใช้ใน SQL Server (RemoteUser) ที่ควรจะสามารถดูข้อมูลได้ (ผ่านแบบสอบถามแบบใช้เลือกข้อมูล) ที่ฟิลด์ LastLoggdInAt ไม่ว่าง

ดูเหมือนว่าฉันจะทำเช่นนี้? เป็นไปได้ไหม?


นี่คือหัวข้อ Books Online สำหรับการรักษาความปลอดภัยระดับแถว: docs.microsoft.com/en-us/sql/relational-database/security/…
David Browne - Microsoft

คำตอบ:


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;

ตอนนี้เราจะแทรกแถวทดสอบสองแถว:

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ฟังก์ชันก่อน


คำตอบที่ยอดเยี่ยม - ขอบคุณ! ความหมายของประสิทธิภาพของ 2 วิธีนี้คืออะไร เราพบว่าเว็บแอปของเราช้าลงถึง 5 เท่าเมื่อเราใช้ฟังก์ชั่นนี้ ต้องดูที่วิธีการดู
LiamB

ฟังก์ชันความปลอดภัยระดับแถวถูกประเมินสำหรับแต่ละแถวทุกแถวที่อ่านจากตาราง ฉันคาดหวังให้การเข้าถึงตารางนั้นช้าลงอย่างมาก ในทางกลับกันมุมมองควรมีผลกระทบเล็กน้อยต่อประสิทธิภาพสมมติว่าคุณสร้างดัชนีที่มีประโยชน์ในLastLoggedInAtคอลัมน์
Max Vernon

นั่นสมเหตุสมผลแล้ว - ฉันจะดูตอนนี้ดูเหมือนว่าจะทำงานได้ดี! หากเราต้องการให้ผู้ใช้สามารถแก้ไขข้อมูลผู้ใช้สำหรับแถวเหล่านี้ที่ตรงกับเกณฑ์ที่จะเป็นไปได้กับมุมมอง
LiamB

มันสวยงามใช้งานได้ดี - ขอบคุณสำหรับความช่วยเหลือนี้
LiamB

ใช่คุณสามารถแก้ไขแถวผ่านมุมมองได้
Max Vernon
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.