Kontrola dostępu oparta na rolach a uprawnieniami

44

Próbuję zrozumieć nieodłączny kompromis między rolami i uprawnieniami, jeśli chodzi o kontrolę dostępu (autoryzację).

Zacznijmy od danego: w naszym systemie pozwolenie będzie drobnoziarnistą jednostką dostępu („ Edytuj zasób X ”, „ Uzyskaj dostęp do strony panelu kontrolnego ” itp.). Rola będzie zbiorem 1+ uprawnienia. Użytkownik może mieć 1+ role. Wszystkie te relacje (użytkownicy, role, uprawnienia) są przechowywane w bazie danych i mogą być zmieniane na bieżąco i w razie potrzeby.

Moje troski:

(1) Co jest takiego „złego” w sprawdzaniu ról kontroli dostępu? Jakie korzyści można uzyskać, sprawdzając zamiast tego uprawnienia? Innymi słowy, jaka jest różnica między tymi dwoma fragmentami poniżej:

if(SecurityUtils.hasRole(user)) {
    // Grant them access to a feature
}

// vs.
if(SecurityUtils.hasPermission(user)) {
    // Grant them access to a feature
}

I:

(2) Jaką przydatną wartość Role w tym scenariuszu zapewniają? Czy nie moglibyśmy po prostu bezpośrednio przypisać uprawnień 1+ użytkownikom? Jaką konkretną wartość abstrakcji oferuje Role (czy ktoś może podać konkretne przykłady)?

smeeb
źródło
2
Kilka punktów: (1) pojedynczy użytkownik może mieć wiele ról, (2) możesz zajrzeć do ACL (Listy kontroli dostępu), np. możesz chcieć zezwolić na „Dostęp do strony panelu” tylko podzestawowi stron panelu (jeśli jest ich kilka).
Matthieu M.,

Odpowiedzi:

62

(1) Co jest takiego „złego” w sprawdzaniu ról kontroli dostępu? Jakie korzyści można uzyskać, sprawdzając zamiast tego uprawnienia?

W momencie sprawdzania kod wywołujący musi tylko wiedzieć „czy użytkownik X ma uprawnienia do wykonania akcji Y?” .
Kod wywołujący nie przejmuje się i nie powinien być świadomy relacji między rolami a uprawnieniami.

Warstwa autoryzacyjna sprawdzi następnie, czy użytkownik ma to uprawnienie, zwykle sprawdzając, czy rola użytkownika ma to uprawnienie. Umożliwia to zmianę logiki autoryzacji bez aktualizacji kodu wywołującego.

Jeśli bezpośrednio sprawdzasz rolę w witrynie wywołującej, domyślnie tworzysz relacje między uprawnieniami do roli i wstrzykujesz logikę autoryzacji do kodu wywołującego, co narusza rozdzielenie obaw.

Jeśli później zdecydujesz, że rola foonie powinna mieć uprawnień baz, będziesz musiał zmienić każdy kod, który sprawdza, czy użytkownik jest użytkownikiem foo.

(2) Jaką przydatną wartość Role w tym scenariuszu zapewniają? Czy nie moglibyśmy po prostu bezpośrednio przypisać uprawnień 1+ użytkownikom? Jaką konkretną wartość abstrakcji oferuje Role (czy ktoś może podać konkretne przykłady)?

Role koncepcyjnie reprezentują nazwaną kolekcję uprawnień.

Załóżmy, że dodajesz nową funkcję, która pozwala użytkownikowi edytować niektóre ustawienia. Ta funkcja powinna być dostępna tylko dla administratorów.

Jeśli przechowujesz uprawnienia na użytkownika, musisz znaleźć wszystkich użytkowników w bazie danych, którzy w jakiś sposób znasz administratorów (jeśli nie przechowujesz informacji o rolach dla użytkowników, skąd miałbyś wiedzieć, którzy użytkownicy są administratorami?) I dołączyć to uprawnienie do ich listy uprawnień.

Jeśli używasz ról, musisz jedynie dołączyć uprawnienia do Administratorroli, która jest zarówno łatwiejsza do wykonania, bardziej wydajna pod względem miejsca, jak i mniej podatna na błędy.

Rotem
źródło
Co? Warstwa uwierzytelnienia sprawdzi, czy to użytkownik twierdzi, że jest; warstwą, która sprawdza, które funkcje / dane może uzyskać dostęp taki użytkownik, jest warstwa autoryzacyjna
SJuan76,
4
Powinno to być obowiązkowe czytanie dla wszystkich programistów. Doskonały.
Kosta Kontos
2
Prosty, zwięzły i na temat - gdzieś bije cały rozdział książki. Dzięki.
Dan Nissenbaum
2
Komentarz dla jasności (i proszę mnie poprawić, jeśli się mylę): authorization layerPrawdopodobnie oznacza nic więcej niż po prostu definicję funkcji (tj.) user->hasPermission(SOME_PERMISSION)Najpierw sprawdź role użytkownika, a następnie sprawdź, czy którakolwiek z ról zawiera / wyklucza podaną pozwolenie. Na przykład, the calling codemożna sprawdzić, czy dana strona jest widoczna dla użytkownika i nazywają user->hasPermission(VIEW_GIVEN_PAGE), a authorization layerskłada się z definicją z hasPermissionfunkcji, która sprawdza role jak wyżej.
Dan Nissenbaum
1
@ DanNissenbaum Tak, brzmi to tak, jakbyś miał rację, może to być tak proste, jak sprawdzenie, czy rola użytkownika ma tę autoryzację. To może być coś więcej. Na przykład może masz opcję tymczasowego zawieszenia użytkownika i w takim przypadku możesz hasPermissionto sprawdzić usersRole.HasPermission(VIEW_GIVEN_PAGE) && !user.Suspended. Chodzi o to, że wszystko odbywa się w jednym miejscu, a nie w konsumującym (wywołującym) kodzie.
Rotem
18

W odpowiedzi na twoje pierwsze pytanie, największym problemem ze sprawdzeniem, czy użytkownik ma rolę, a nie konkretne uprawnienia, jest to, że uprawnienia mogą być przechowywane przez wiele ról. Na przykład programista może mieć dostęp do portalu dla deweloperów w intranecie firmy, co prawdopodobnie jest również pozwoleniem posiadanym przez jego menedżera. Jeśli użytkownik próbuje uzyskać dostęp do portalu dla deweloperów, masz czek podobny do:

if(SecurityUtils.hasRole(developer)) {
    // Grant them access to a feature
} else if(SecurityUtils.hasRole(manager)) {
    // Grant them access to a feature
} else if...

( switchWypowiedź w wybranym przez Ciebie języku byłaby lepsza, ale nadal niezbyt uporządkowana)

Im bardziej powszechne lub powszechnie posiadane jest uprawnienie, tym więcej ról użytkowników należy sprawdzić, aby upewnić się, że ktoś może uzyskać dostęp do danego systemu. Doprowadziłoby to również do problemu polegającego na tym, że za każdym razem, gdy modyfikujesz uprawnienia do roli, musisz zmodyfikować czek, aby to odzwierciedlić. W dużym systemie bardzo szybko stałoby się to niewygodne.

Jeśli po prostu sprawdzisz, czy użytkownik ma uprawnienia, które zezwalają mu na przykład na dostęp do portalu dla programistów, nie ma znaczenia, jaką rolę pełnią, zostanie mu przyznany dostęp.

Aby odpowiedzieć na drugie pytanie, masz role, ponieważ działają one tak łatwo, jak modyfikować i dystrybuować „pakiety” uprawnień. Jeśli masz system, który ma setki ról i tysiące uprawnień, dodanie nowego użytkownika (na przykład nowego menedżera HR) wymagałoby przejścia przez użytkownika i przyznania mu każdego pozwolenia, które posiadają inni menedżerowie HR. Byłoby to nie tylko uciążliwe, ale również podatne na błędy, gdyby wykonano je ręcznie. Porównaj to z prostym dodaniem roli „HR manager” do profilu użytkownika, który zapewni mu taki sam dostęp jak każdy inny użytkownik z tą rolą.

Możesz argumentować, że możesz po prostu sklonować istniejącego użytkownika (jeśli twój system to obsługiwał), ale chociaż daje to użytkownikowi odpowiednie uprawnienia na ten moment, próba dodania lub usunięcia uprawnienia dla wszystkich użytkowników w przyszłości może być trudny. Przykładowym scenariuszem jest sytuacja, w której być może w przeszłości personel HR był również odpowiedzialny za płace, ale później firma staje się wystarczająco duża, aby zatrudnić pracowników specjalnie do obsługi listy płac. Oznacza to, że dział HR nie musi już uzyskiwać dostępu do systemu płac, aby zezwolenie można było usunąć. Jeśli masz 10 różnych członków HR, musisz ręcznie przejść i upewnić się, że usunąłeś odpowiednie uprawnienie, które wprowadza możliwość błędu użytkownika. Innym problemem jest to, że po prostu nie skaluje się; w miarę zdobywania coraz większej liczby użytkowników w danej roli znacznie utrudnia to modyfikację roli. Porównaj to z użyciem ról, w których wystarczy zmodyfikować nadrzędną rolę, o której mowa, aby usunąć uprawnienie, co będzie odzwierciedlone przez każdego użytkownika, który pełni tę rolę.

Matt Champion
źródło
dobry przykład, dzięki!
szczery