تحديد الأمان على مستوى الصف

مكتمل

لا يستخدم الأمان على مستوى الصف (RLS) التشفير ويعمل على مستوى قاعدة البيانات لتقييد الوصول إلى جدول باستخدام نهج أمان يستند إلى عضوية المجموعة أو سياق التخويل. هذا وظيفيًا يعادل عبارة WHERE.

يستدعي نهج الأمان دالة قيم الجدول مُضمنة لحماية الوصول إلى الصفوف في الجدول.

اعتمادًا على سمة المستخدم، تحدد دالة التقييم ما إذا كان هذا المستخدم لديه حق الوصول إلى المعلومات ذات الصلة. عند تشغيل استعلام مقابل جدول، يُطبق نهج الأمان دالة التقييم. اعتمادًا على متطلبات العمل، يمكن أن يكون RLS بسيطًا مثل WHERE CustomerId = 29 أو معقدًا كما هو مطلوب.

يُوجد نوعان من نُهج الأمان التي يدعمها الأمان على مستوى الصف:

  • دوال تقييم التصفية - تقييد الوصول إلى البيانات التي تنتهك دالة التقييم.

    Access التعريف
    حدد ⁧. لا يمكن عرض الصفوف التي تمت تصفيتها.
    تحديث لا يمكن تحديث الصفوف التي تمت تصفيتها.
    حذف لا يمكن حذف الصفوف التي تمت تصفيتها.
    إدراج غير قابل للتطبيق.
  • دوال تقييم الحظر - تقييد تغييرات البيانات التي تنتهك دالة التقييم.

    Access التعريف
    بعد الإدراج منع المستخدمين من إدراج صفوف ذات قيم تنتهك دالة التقييم.
    بعد التحديث منع المستخدمين من تحديث صفوف إلى قيم تنتهك دالة التقييم.
    قبل التحديث منع المستخدمين من تحديث الصفوف التي تنتهك دالة التقييم.
    قبل الحذف حظر عمليات الحذف إذا كان الصف ينتهك دالة التقييم.

نظرًا لتكوين التحكم بالوصول وتطبيقه على مستوى قاعدة البيانات، فإن تغييرات التطبيق ضئيلة - إن وجدت. أيضًا، يمكن للمستخدمين الوصول مباشرة إلى الجداول ويمكنهم الاستعلام عن بياناتهم.

يُنفذ الأمان على مستوى الصف في ثلاث خطوات رئيسية:

  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;

بعد ذلك، أنشئ المستخدمين وامنحهم حق الوصول إلى جدول "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" جميع الصفوف. يجب أن يرى مستخدمو Tenant1وTenant2وTenant3وTenant4 صفوفهم الخاصة فقط.

إذا قمت بتغيير نهج الأمان باستخدام WITH (STATE = OFF);، فستلاحظ أن المستخدمين يرون جميع الصفوف.

لقطة شاشة لأوامر T-SQL لتغيير نهج الأمان.

إشعار

هناك خطر تسرب المعلومات إذا كتب المهاجم استعلاما يحتوي على عبارة مصممة WHERE خصيصا ، وعلى سبيل المثال ، خطأ قسمة على صفر ، لفرض استثناء إذا كان الشرط WHERE صحيحا. يعرف هذا باسم هجوم القناة الجانبية. من الحكمة الحد من قدرة المستخدمين على تشغيل استعلامات غير مخطط لها عند استخدام الأمان على مستوى الصف.

حالة الاستخدام

يعد الأمان على مستوى الصف مثاليًا للعديد من السيناريوهات، بما في ذلك:

  • عندما تحتاج إلى عزل وصول الأقسام على مستوى الصف.
  • عندما تحتاج إلى تقييد وصول بيانات العملاء إلى البيانات ذات الصلة بشركتهم فقط.
  • عندما تحتاج إلى تقييد الوصول لأغراض التوافق.

أفضل الممارسات

فيما يلي بعض أفضل الممارسات التي يجب مراعاتها عند تنفيذ RLS:

  • إنشاء مخطط منفصل لوظائف المسند ونهج الأمان.
  • تجنب تحويلات الكتابة في دالات المسند.
  • تجنب استخدام وصلات الجدول المفرطة والعودية في وظائف المسند.