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

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

การรักษาความปลอดภัยระดับแถว (RLS) ไม่ได้ใช้การเข้ารหัสลับและดําเนินการในระดับฐานข้อมูลเพื่อจํากัดการเข้าถึงตารางโดยใช้นโยบายความปลอดภัยที่ยึดตามการเป็นสมาชิกกลุ่มหรือบริบทการอนุญาต ฟังก์ชันนี้จะเทียบเท่ากับส่วนคำสั่ง WHERE

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

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

มีนโยบายความปลอดภัยสองชนิดที่ได้รับการสนับสนุนโดยการรักษาความปลอดภัยระดับแถว:

  • เพรดิเคตตัวกรอง - จํากัดการเข้าถึงข้อมูลที่ละเมิดเพรดิเคต

    เข้าถึง คำจำกัดความ
    เลือก ไม่สามารถดูแถวที่ถูกกรอง
    อัปเดต ไม่สามารถอัปเดตแถวที่ถูกกรองได้
    ลบ ไม่สามารถลบแถวที่มีการกรองได้
    แทรก N/A
  • บล็อกเพรดิเคต - จํากัดการเปลี่ยนแปลงข้อมูลที่ละเมิดเพรดิเคต

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

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

การรักษาความปลอดภัยระดับแถวมีการใช้งานในสามขั้นตอนหลัก:

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

คําสั่ง T-SQL ต่อไปนี้สาธิตวิธีใช้ RLS ในสถานการณ์ที่การเข้าถึงของผู้ใช้ถูกแยกจากผู้เช่า:

-- Create supporting objects for this example
CREATE TABLE [Sales] (SalesID INT, 
    ProductID INT, 
    TenantName NVARCHAR(10), 
    OrderQtd INT, 
    UnitPrice MONEY)
GO

INSERT INTO [Sales]  VALUES (1, 3, 'Tenant1', 5, 10.00);
INSERT INTO [Sales]  VALUES (2, 4, 'Tenant1', 2, 57.00);
INSERT INTO [Sales]  VALUES (3, 7, 'Tenant1', 4, 23.00);
INSERT INTO [Sales]  VALUES (4, 2, 'Tenant2', 2, 91.00);
INSERT INTO [Sales]  VALUES (5, 9, 'Tenant3', 5, 80.00);
INSERT INTO [Sales]  VALUES (6, 1, 'Tenant3', 5, 35.00);
INSERT INTO [Sales]  VALUES (7, 3, 'Tenant4', 8, 11.00);

-- View all the rows in the table  
SELECT * FROM Sales;

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

CREATE USER [TenantAdmin] WITH PASSWORD = '<strong password>'
GO
CREATE USER [Tenant1] WITH PASSWORD = '<strong password>'
GO
CREATE USER [Tenant2] WITH PASSWORD = '<strong password>'
GO
CREATE USER [Tenant3] WITH PASSWORD = '<strong password>'
GO
CREATE USER [Tenant4] WITH PASSWORD = '<strong password>'
GO

GRANT SELECT ON [Sales] TO [TenantAdmin]
GO
GRANT SELECT ON [Sales] TO [Tenant1]
GO
GRANT SELECT ON [Sales] TO [Tenant2]
GO
GRANT SELECT ON [Sales] TO [Tenant3]
GO
GRANT SELECT ON [Sales] TO [Tenant4]
GO

ถัดไป เราจะสร้างสคีมาใหม่ ฟังก์ชันที่ให้ค่าตารางแบบอินไลน์ และอนุญาตให้ผู้ใช้เข้าถึงฟังก์ชันใหม่ เพรดิเคต WHERE @TenantName = USER_NAME() OR USER_NAME() = 'TenantAdmin' จะประเมินถ้าชื่อผู้ใช้ที่ดําเนินการคิวรีตรงกับค่าคอลัมน์ TenantName

CREATE SCHEMA sec;  
GO  

--Create the filter predicate

CREATE FUNCTION sec.tvf_SecurityPredicatebyTenant(@TenantName AS NVARCHAR(10))  
    RETURNS TABLE  
WITH SCHEMABINDING  
AS  
    RETURN	SELECT 1 AS result
			WHERE @TenantName = USER_NAME() OR USER_NAME() = 'TenantAdmin';  
GO

--Grant users access to inline table-valued function

GRANT SELECT ON sec.tvf_SecurityPredicatebyTenant TO [TenantAdmin]
GO
GRANT SELECT ON sec.tvf_SecurityPredicatebyTenant TO [Tenant1]
GO
GRANT SELECT ON sec.tvf_SecurityPredicatebyTenant TO [Tenant2]
GO
GRANT SELECT ON sec.tvf_SecurityPredicatebyTenant TO [Tenant3]
GO
GRANT SELECT ON sec.tvf_SecurityPredicatebyTenant TO [Tenant4]
GO

--Create security policy and add the filter predicate
CREATE SECURITY POLICY sec.SalesPolicy  
ADD FILTER PREDICATE sec.tvf_SecurityPredicatebyTenant(TenantName) ON [dbo].[Sales]
WITH (STATE = ON);  
GO

ในตอนนี้ เราพร้อมที่จะทดสอบการเข้าถึง:

EXECUTE AS USER = 'TenantAdmin';  
SELECT * FROM dbo.Sales;
REVERT;  
  
EXECUTE AS USER = 'Tenant1';  
SELECT * FROM dbo.Sales;
REVERT;  
  
EXECUTE AS USER = 'Tenant2';  
SELECT * FROM dbo.Sales;
REVERT;

EXECUTE AS USER = 'Tenant3';  
SELECT * FROM dbo.Sales;
REVERT;

EXECUTE AS USER = 'Tenant4';  
SELECT * FROM dbo.Sales;
REVERT;

ผู้ใช้ TenantAdmin ควรเห็นแถวทั้งหมด ผู้ใช้ ผู้เช่า 1, ผู้เช่า 2, ผู้เช่า 3 และ ผู้เช่า 4 ควรเห็นเฉพาะแถวของตนเองเท่านั้น

หากคุณเปลี่ยนนโยบายความปลอดภัยด้วย WITH (STATE = OFF);คุณจะสังเกตเห็นว่าผู้ใช้เห็นแถวทั้งหมด

สกรีนช็อตของคําสั่ง T-SQL เพื่อเปลี่ยนนโยบายความปลอดภัย

หมายเหตุ

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

กรณีการใช้งาน

การรักษาความปลอดภัยระดับแถวเหมาะสําหรับหลายสถานการณ์ รวมถึง:

  • เมื่อคุณต้องการแยกการเข้าถึงข้อมูลของแผนกในระดับแถว
  • เมื่อคุณต้องการจํากัดการเข้าถึงข้อมูลของลูกค้าให้เหลือเพียงข้อมูลที่เกี่ยวข้องกับบริษัทของพวกเขาเท่านั้น
  • เมื่อคุณต้องการจํากัดการเข้าถึงเพื่อวัตถุประสงค์ด้านการปฏิบัติตามกฎระเบียบ

แนวทางปฏิบัติ

ต่อไปนี้คือหลักปฏิบัติที่ดีที่สุดบางประการที่ควรพิจารณาเมื่อใช้ RLS:

  • สร้าง Schema แยกต่างหากสําหรับฟังก์ชันเพรดิเคต และนโยบายความปลอดภัย
  • หลีกเลี่ยงการแปลงชนิดในฟังก์ชันเพรดิเคต
  • หลีกเลี่ยงการใช้การรวมตารางที่มากเกินไปและการเรียกใช้ซ้ําในฟังก์ชันเพรดิเคต