ใช้การรักษาความปลอดภัยระดับแถว

เสร็จสมบูรณ์เมื่อ

Row-Level Security (RLS) ช่วยให้คุณสามารถควบคุมการเข้าถึงแถวในตารางฐานข้อมูลโดยยึดตามลักษณะของผู้ใช้ที่ดําเนินการแบบสอบถาม ซึ่งแตกต่างจากสิทธิ์ระดับตารางที่ให้หรือปฏิเสธการเข้าถึงทั้งตาราง RLS จะกรองแถวแบบไดนามิกเพื่อให้ผู้ใช้เห็นเฉพาะข้อมูลที่ได้รับอนุญาตให้เข้าถึงเท่านั้น

ความสามารถนี้มีประโยชน์เมื่อผู้ใช้หรือผู้เช่าหลายคนใช้ตารางเดียวกัน แต่ควรดูเฉพาะข้อมูลของตนเองเท่านั้น ตัวแทนฝ่ายขายจะเห็นบันทึกลูกค้าของตนเอง ผู้จัดการจะเห็นข้อมูลของทีม และผู้อํานวยการระดับภูมิภาคจะเห็นข้อมูลทั้งหมดสําหรับภูมิภาคของตน การกรองจะเกิดขึ้นโดยอัตโนมัติโดยไม่ต้องเปลี่ยนรหัสแอปพลิเคชัน

ทําความเข้าใจส่วนประกอบ RLS

Row-Level Security ใช้สององค์ประกอบที่ทํางานร่วมกัน: เพรดิเคตความปลอดภัยและนโยบายความปลอดภัย

ไดอะแกรมแสดงความปลอดภัยของ Row-Level ในฐานข้อมูลแบบหลายผู้เช่าที่ผู้ใช้สามคนสอบถามตารางลูกค้าเดียวกัน แต่แต่ละคนจะเห็นเฉพาะแถวของผู้เช่า ซึ่งกรองโดยส่วนประกอบนโยบายความปลอดภัย

เพรดิเคตความปลอดภัยคือฟังก์ชันที่มีค่าตารางแบบอินไลน์ที่ส่งกลับ 1 (จริง) หรือ 0 (เท็จ) สําหรับแต่ละแถว โดยจะรับบริบทของผู้ใช้ปัจจุบันและค่าแถว จากนั้นจะตัดสินใจว่าผู้ใช้รายนั้นควรเห็นแถวหรือไม่ คิดว่ามันเป็นตรรกะทางธุรกิจของคุณสําหรับการเข้าถึงข้อมูล ซึ่งบรรจุเป็นฟังก์ชัน

นโยบายความปลอดภัยจะผูกฟังก์ชันเพรดิเคตของคุณกับตารางและระบุชนิดของการกรอง คุณสามารถสร้างเพ รดิเคตตัวกรอง ที่แยกแถวที่ไม่ได้รับอนุญาตออกจากผลลัพธ์แบบสอบถามอย่างเงียบๆ หรือ บล็อกเพรดิเคต ที่ป้องกันการแทรก ปรับปรุง และลบโดยไม่ได้รับอนุญาต

ตัวกรองเพรดิเคตมีผล SELECTและUPDATEDELETEคําสั่งโดยการลบแถวที่ผู้ใช้ไม่สามารถเข้าถึงได้ ผู้ใช้จะไม่ได้รับข้อผิดพลาด แต่จะเห็นเพียงชุดผลลัพธ์ที่กรองแล้ว เพรดิเคตบล็อกทํางานต่างกัน: ทําให้เกิดข้อผิดพลาดเมื่อผู้ใช้พยายามเปลี่ยนแปลงโดยไม่ได้รับอนุญาต

สร้างเพรดิเคตตัวกรอง

เริ่มต้นด้วยการสร้างฟังก์ชันเพรดิเคตที่ประเมินการเข้าถึงแถว ฟังก์ชันยอมรับพารามิเตอร์ที่แสดงค่าคอลัมน์เพื่อตรวจสอบและส่งคืนตารางที่มีแถวเดียวเมื่ออนุญาตให้เข้าถึง

นี่คือสถานการณ์ทั่วไป: แอปพลิเคชันแบบหลายผู้เช่าที่แต่ละแถวมี TenantID คอลัมน์:

CREATE SCHEMA Security;
GO

CREATE FUNCTION Security.fn_TenantAccessPredicate(@TenantID int)
RETURNS TABLE
WITH SCHEMABINDING
AS
RETURN SELECT 1 AS fn_TenantAccessPredicate_Result
    WHERE @TenantID = CAST(SESSION_CONTEXT(N'TenantID') AS int);

ฟังก์ชันนี้จะตรวจสอบว่าแถว TenantID ตรงกับค่าที่เก็บไว้ในบริบทของเซสชันหรือไม่ แอปพลิเคชันของคุณตั้งค่าบริบทนี้หลังจากที่ผู้ใช้ตรวจสอบสิทธิ์:

EXEC sp_set_session_context @key = N'TenantID', @value = 42;

สําหรับสถานการณ์ที่อิงตามผู้ใช้ฐานข้อมูลมากกว่าบริบทของเซสชัน เพรดิเคตสามารถอ้างอิงผู้ใช้ปัจจุบันได้:

CREATE FUNCTION Security.fn_SalesRepPredicate(@SalesRepID int)
RETURNS TABLE
WITH SCHEMABINDING
AS
RETURN SELECT 1 AS fn_SalesRepPredicate_Result
    WHERE @SalesRepID = DATABASE_PRINCIPAL_ID()
       OR IS_MEMBER('SalesManagers') = 1;

เพรดิเคตนี้ช่วยให้ตัวแทนฝ่ายขายสามารถดูเรกคอร์ดของตนเองในขณะที่ผู้จัดการในบทบาทสามารถ SalesManagers ดูเรกคอร์ดทั้งหมดได้

สร้างนโยบายความปลอดภัย

เมื่อคุณกําหนดฟังก์ชันเพรดิเคตของคุณแล้ว ให้สร้างนโยบายความปลอดภัยที่นําไปใช้กับตาราง:

CREATE SECURITY POLICY TenantSecurityPolicy
ADD FILTER PREDICATE Security.fn_TenantAccessPredicate(TenantID)
    ON dbo.Orders,
ADD FILTER PREDICATE Security.fn_TenantAccessPredicate(TenantID)
    ON dbo.OrderDetails
WITH (STATE = ON);

นโยบายนี้กรองทั้งตาราง Orders และ OrderDetails ตารางโดยใช้ฟังก์ชันเพรดิเคตเดียวกัน เมื่อผู้ใช้คิวรีตารางเหล่านี้ พวกเขาจะเห็นเฉพาะแถวที่ตรงกับบริบทของผู้เช่าเท่านั้น

คุณสามารถรวมตัวกรองและบล็อกเพรดิเคตไว้ในนโยบายเดียว:

CREATE SECURITY POLICY SalesSecurityPolicy
ADD FILTER PREDICATE Security.fn_SalesRepPredicate(SalesRepID)
    ON dbo.CustomerAccounts,
ADD BLOCK PREDICATE Security.fn_SalesRepPredicate(SalesRepID)
    ON dbo.CustomerAccounts AFTER INSERT,
ADD BLOCK PREDICATE Security.fn_SalesRepPredicate(SalesRepID)
    ON dbo.CustomerAccounts AFTER UPDATE
WITH (STATE = ON);

ทําไมต้องเพิ่มเพรดิเคตบล็อก? หากไม่มีพวกเขา ผู้ใช้สามารถแทรกแถวที่มีแถวอื่น SalesRepID และสูญเสียการเข้าถึงข้อมูลที่เพิ่งสร้างขึ้น เพรดิเคตบล็อกช่วยให้แน่ใจว่าผู้ใช้สามารถแทรกหรืออัปเดตได้เฉพาะแถวที่พวกเขาสามารถดูได้

ใช้รูปแบบการเข้าถึงแบบลําดับชั้น

หลายองค์กรต้องการการเข้าถึงข้อมูลแบบลําดับชั้น ผู้จัดการควรดูข้อมูลของผู้ใต้บังคับบัญชา คุณสามารถนําไปใช้ได้โดยการรวม RLS กับตารางลําดับชั้นการจัดการ

นี่คือฟังก์ชันที่ข้ามลําดับชั้น:

CREATE FUNCTION Security.fn_HierarchyPredicate(@OwnerID int)
RETURNS TABLE
WITH SCHEMABINDING
AS
RETURN
    WITH EmployeeHierarchy AS (
        SELECT EmployeeID, ManagerID
        FROM dbo.Employees
        WHERE EmployeeID = DATABASE_PRINCIPAL_ID()
        
        UNION ALL
        
        SELECT e.EmployeeID, e.ManagerID
        FROM dbo.Employees e
        INNER JOIN EmployeeHierarchy h ON e.ManagerID = h.EmployeeID
    )
    SELECT 1 AS fn_HierarchyPredicate_Result
    WHERE @OwnerID IN (SELECT EmployeeID FROM EmployeeHierarchy);

นิพจน์ตารางทั่วไปแบบเรียกซ้ํา (CTE) นี้สร้างห่วงโซ่ทั้งหมดของพนักงานที่รายงานไปยังผู้ใช้ปัจจุบัน เพรดิเคตอนุญาตให้เข้าถึงแถวใดๆ ที่เป็นเจ้าของโดยบุคคลในลําดับชั้นนั้น

Note

เพรดิเคตแบบเรียกซ้ําอาจส่งผลต่อประสิทธิภาพของคิวรีในชุดข้อมูลขนาดใหญ่ พิจารณาการแคชความสัมพันธ์ตามลําดับชั้นหรือจํากัดความลึกของการเรียกซ้ําเพื่อประสิทธิภาพที่ดีขึ้น

จัดการนโยบายความปลอดภัย

นโยบายความปลอดภัยสนับสนุนการดําเนินการจัดการหลายอย่างสําหรับการบํารุงรักษาอย่างต่อเนื่อง คุณปิดใช้นโยบายชั่วคราวได้โดยไม่ต้องนําออกดังนี้

ALTER SECURITY POLICY TenantSecurityPolicy
WITH (STATE = OFF);

คุณสามารถเพิ่มเพรดิเคตใหม่ให้กับนโยบายที่มีอยู่ได้ดังนี้

ALTER SECURITY POLICY TenantSecurityPolicy
ADD FILTER PREDICATE Security.fn_TenantAccessPredicate(TenantID)
    ON dbo.Shipments;

หรือลบเพรดิเคตเมื่อตารางไม่ต้องการการกรองอีกต่อไป:

ALTER SECURITY POLICY TenantSecurityPolicy
DROP FILTER PREDICATE ON dbo.Orders;

เมื่อต้องการดูนโยบายความปลอดภัยที่มีอยู่และเพรดิเคท:

SELECT p.name AS PolicyName,
       p.is_enabled,
       o.name AS TableName,
       pred.predicate_definition
FROM sys.security_policies p
INNER JOIN sys.security_predicates pred ON p.object_id = pred.object_id
INNER JOIN sys.objects o ON pred.target_object_id = o.object_id;

เคล็ดลับ

เมื่อแก้ไขปัญหา RLS ให้ปิดใช้งานนโยบายความปลอดภัยชั่วคราวและเปรียบเทียบผลลัพธ์ ซึ่งจะช่วยระบุว่าผลลัพธ์ที่ไม่คาดคิดมาจากเพรดิเคต RLS หรือตรรกะแบบสอบถามอื่นๆ หรือไม่

Row-Level Security ทํางานอย่างโปร่งใสกับมุมมอง กระบวนงานที่เก็บไว้ และวัตถุฐานข้อมูลอื่นๆ ที่สอบถามตารางที่ได้รับการป้องกัน ผู้ใช้ที่เข้าถึงข้อมูลผ่านเส้นทางใดๆ จะได้รับผลลัพธ์ที่กรองอย่างสม่ําเสมอตามบริบทด้านความปลอดภัย