Защо сигурността на ниво ред има значение?
Преди SQL Server 2016 защитата на ниво таблица беше най-ниското ниво на сигурност по подразбиране за база данни. С други думи, потребителят може да бъде ограничен за достъп до таблица като цяло. Въпреки това, в някои случаи се нуждаем от потребителите да имат достъп до таблица, но не и до конкретни редове в таблицата. Преди SQL Server 2016 това изискваше да бъдат написани персонализирани съхранени процедури за осигуряване на такава фино зърнеста сигурност. Такива съхранени процедури обаче са склонни към SQL инжектиране и други предупреждения за сигурност.
Използване на функцията за сигурност на ниво ред на SQL Server на практика
SQL Server 2016 въведе нова функция за защита на ниво ред, която позволява на потребителите да имат достъп до таблица, но ги ограничава до достъп до конкретни редове в тази таблица. Нека да разгледаме как това може да се използва на практика.
Описание
Има четири стъпки за внедряване на защита на ниво ред в SQL Server.
- Предоставете разрешения за избор на потребителите в таблицата, на която искате да внедрите защита на ниво ред.
- След това трябва да напишете функция за стойност на вградена таблица, съдържаща предикат на филтър. Добавете логиката на филтъра към предиката на филтъра.
- Накрая трябва да свържете предиката на филтъра, който сте създали във втората стъпка, с политика за сигурност.
- Тествайте функцията за защита на ниво ред.
Преди да изпълним горните стъпки, трябва да създадем фиктивна база данни с някои фиктивни записи. За да направите това, изпълнете следния скрипт:
СЪЗДАВАНЕ НА БАЗА ДАННИ UniversityGOUSE UniversityGOUSE UniversityСЪЗДАВАНЕ НА ТАБЛИЦА Лица(Id INT ПРАВИЛЕН КЛЮЧ ИДЕНТИФИКАЦИЯ(1,1),Име VARCHAR (50),Роля VARCHAR (50))GOUSE UniversityINSERT INTO People VALUES ('Sally', 'Principal' INTO ) ЦЕННОСТИ НА ЛИЧАТА ('Едуард', 'Студент' )ВЪВЕТЕ ВЪВ ЦЕННОСТИ НА Лицата ('Джон', 'Студент' )ВЪВЕТЕ В ЦЕННОСТИ НА Лицата ('Шотландец', 'Студент') ВЪВЕТЕ В ЦЕННОСТИ НА Лицата ('Бен', 'Студент') ВЪВЕТЕ В ЦЕННОСТИ НА Лицата ('Изабел', 'Учител' )ВМЕВЕТЕ В ЦЕННОСТИ НА ЛИЧА ('Дейвид', 'Учител' )ВМЕВЕТЕ В ЦЕННОСТИ НА Лицата ('Лора', 'Учител' )ВЪВЕТЕ В СТОЙНОСТИТЕ НА Лицата ('Teacher', ')ВЪВЕДЕТЕ В ЦЕННОСТИ НА личности („Франсис“, „Учител“)
В скрипта създаваме фиктивна база данни „Университет“. След това изпълняваме скрипта, който създава таблица с име „Лица“. Ако погледнете дизайна на таблицата, можете да видите, че тя съдържа три колони Id, Name и Role. Колоната Id е колоната с първичен ключ с ограничение IDENTITY. Колоната Име съдържа името на лицето, а колоната Роля съдържа ролята на лицето. Накрая вмъкнахме 10 записа в таблицата с лицата. В таблицата има 1 директор, 4 учители и 5 ученика.
Нека изпълним прост оператор SELECT, за да видим записи в таблицата:
Използвайте UniversitySELECT * FROM Persons
Резултатът изглежда така:
Искаме потребителят с име Principal да има достъп до всички редове в таблицата с лицата. По същия начин, учителят трябва да има достъп само до записите на учителите, докато учениците трябва да имат достъп само до записите на учениците. Това е класически случай на сигурност на ниво ред.
За да приложим сигурността на ниво ред, ще следваме стъпките, които обсъдихме по-рано.
Стъпка 1:Предоставяне на разрешения за избор на потребители на таблицата
Нека създадем трима потребители с роли Директор, Учител и Ученик и да им предоставим SELECT достъп до тези потребители в таблицата Persons. За да направите това, изпълнете следния скрипт:
СЪЗДАВАНЕ НА ПОТРЕБИТЕЛ РЕЖИНА БЕЗ ВХОД;GOGRANT ИЗБИРАНЕ НА ПОТРЕБИТЕЛ Учител БЕЗ ВХОД;GOGRANT ИЗБИРАНЕ НА ПОТРЕБИТЕЛ Студент БЕЗ ВХОД;GOUSE UniversityGRANT ИЗБОР НА Лицата към Директор;GOGRANT SELECT НА Лицата към Учител;GOGRANT SELECT НА Лицата към Студент;Стъпка 2:Създаване на предикат на филтър
След като на потребителите бъдат предоставени разрешения, следващата стъпка е да създадете предикат за филтриране.
Следният скрипт прави това:
Използвайте UniversityGOCREATE FUNCTION dbo.fn_SP_Person(@Role AS sysname) ВРЪЩА ТАБЛИЦА СЪС SCHEMABINDINGAS RETURN SELECT 1 КАТО fn_SP_Person_output -- Предикатна логика WHERE @Role =USER_NAME() ИЛИ USER_NAME() ='GOPrinci_NAME()';Предикатът на филтъра се създава във вградена функция със стойност на таблица и поема ролята на потребителя като параметър. Той връща онези записи, при които стойността на ролята, предадена като параметър, съвпада със стойността на ролята в колоната Role. Или ако потребителската роля е „Принципал“, всички роли се връщат. Ако погледнете предикатния филтър, няма да намерите името на таблицата, за която създаваме филтъра. Предикатът на филтъра е свързан към таблицата чрез политиката за сигурност, която ще видим в следващата стъпка.
Стъпка 3:Създаване на политика за сигурност
Изпълнете следния скрипт, за да създадете политика за сигурност за предиката на филтъра, който създадохме в последната стъпка:
Използвайте UniversityGoCREATE ПОЛИТИКА ЗА СИГУРНОСТ RoleFilterADD FILTER PREDICATE dbo.fn_SP_Person(Role)ON dbo.PersonsWITH (STATE =ON);GOВ политиката за сигурност просто добавихме предиката на филтъра, който създадохме, към таблицата Persons. За да активирате правилото, флагът „STATE“ трябва да бъде настроен на ON.
Стъпка 4:Тестване на сигурността на ниво ред
Изпълнихме всички стъпки, необходими, за да наложим сигурността на ниво ред в таблицата Persons на университетската база данни. Нека първо опитаме да получим достъп до записите в таблицата с лицата чрез потребител по подразбиране. Изпълнете следния скрипт:
Използвайте UniversitySELECT * FROM Persons;GOНяма да видите нищо в изхода, тъй като потребителят по подразбиране няма достъп до таблицата Persons.
Нека преминем към потребителя на Student, който създадохме по-рано и се опитаме да ИЗБЕРЕМ записи от таблицата Persons:
EXECUTE AS USER ='Студент';Използвайте UniversitySELECT * FROM Persons; -- Само студентски записиREVERT;GOВ скрипта по-горе преминаваме към потребителя „Студент“, избираме записи от таблицата „Лица“ и се връщаме обратно към потребителя по подразбиране. Резултатът изглежда така:
Можете да видите, че поради защитата на ниво ред се показват само записи, в които колоната Роля има стойност Студент.
По същия начин потребителят Учител ще има достъп само до записи, където колоната Роля има стойност Учител. Изпълнете следния скрипт, за да проверите това:
EXECUTE AS USER ='Учител';Използвайте UniversitySELECT * FROM Persons; -- Всички записиREVERT;GOВ изхода ще имате следните записи:
И накрая, в нашия предикат на филтъра, ние внедрихме логиката, че принципалът на потребителя може да има достъп до всички записи. Нека проверим това, като изпълним следната заявка:
EXECUTE AS USER ='Principal';Използвайте UniversitySELECT * FROM Persons; -- Всички записиREVERT;GOВ изхода ще видите всички записи, както е показано по-долу:
Заключение
Функцията за защита на ниво ред е изключително полезна, когато искате потребителите да имат фин достъп до конкретни данни. Функцията за сигурност на ниво ред обаче включва функция с вградена стойност на таблицата, което може да ви накара да постигнете удар в производителността.
Като правило, ако планирате да използвате проста клауза WHERE в предикатната функция, производителността ви не трябва да бъде засегната. От друга страна, сложните оператори за свързване, включващи справочни таблици, трябва да се избягват, когато сте внедрили защита на ниво ред.