Dynamiczna ocena kodu w Javie - Clever czy Sloppy?

30

Próbuję utworzyć elastyczną strukturę ACL w Javie dla mojej aplikacji.

Wiele frameworków ACL jest zbudowanych na białej liście reguł, gdzie reguła ma postać właściciel: akcja: zasób . Na przykład,

  • „JOHN może ZOBACZYĆ zasób FOOBAR-1”
  • „MARY może ZOBACZ zasób FOOBAR-1”
  • „MARY może EDYTOWAĆ zasób FOOBAR-1”

Jest to atrakcyjne, ponieważ reguły można łatwo serializować / utrwalać w bazie danych. Ale moja aplikacja ma złożoną logikę biznesową. Na przykład,

  • „Wszyscy użytkownicy w dziale 1 z ponad 5-letnim stażem pracy mogą ZOBACZ zasób FOOBAR-1, w przeciwnym razie nie są autoryzowani”
  • „Wszyscy użytkownicy w dziale 2, jeśli data jest późniejsza niż 15.03.2016, mogą ZOBACZ zasób FOOBAR-2, w przeciwnym razie nie autoryzowani”

Na pierwszy rzut oka koszmarem byłoby opracowanie schematu bazy danych, który mógłby obsługiwać nieskończenie złożone reguły takie jak te. Dlatego wydaje się, że musiałbym „upiec” je w skompilowanej aplikacji, ocenić dla każdego użytkownika, a następnie wygenerować reguły właściciel: akcja: zasoby w wyniku oceny. Chcę uniknąć upieczenia logiki w skompilowanej aplikacji.

Tak więc myślałem o reprezentowaniu reguły w formie predykatu : akcja: zasób , gdzie predykat jest wyrażeniem logicznym, które określa, czy użytkownik jest dozwolony. Predykat byłby ciągiem wyrażenia JavaScript, który mógłby być oceniany przez silnik Rhino Java. Na przykład,

  • return user.getDept() == 1 && user.seniority > 5;

W ten sposób predykaty można łatwo utrwalić w bazie danych.

Czy to jest sprytne ? Czy to jest niechlujne ? Czy to jest chwytliwe ? Czy to jest przeprojektowane ? Czy jest to bezpieczne (podobno Java może piaskownic silnik Rhino).

Twittopher
źródło
8
Jaka jest korzyść z wypchnięcia tych reguł biznesowych do bazy danych w porównaniu z logiką w skompilowanej aplikacji?
Winston Ewert
6
@WinstonEWert Eksternalizacja reguł eliminuje potrzebę ponownej kompilacji i redystrybucji aplikacji w przypadku zmiany, dodania lub usunięcia reguły.
Twittopher
2
Interesujące pytanie! Chciałbym zobaczyć odpowiedź, która nie skupia się tak bardzo na bezpieczeństwie, ale raczej na aspektach utrzymania, niezawodności i łatwości użytkowania takiego rozwiązania.
oliver
6
To brzmi podobnie do reguł poczty e-mail programu Outlook, które jest zasadniczo mechanizmem reguł konfigurowanym przez użytkownika.

Odpowiedzi:

37

Pipowanie dynamicznych danych do interpretera języka implementacji jest zwykle złym pomysłem, ponieważ zwiększa ryzyko uszkodzenia danych do możliwości przejęcia złośliwych aplikacji. Innymi słowy, idziesz na drodze do stworzenia do wstrzyknięcia kodu usterki.

Twój problem można lepiej rozwiązać za pomocą mechanizmu reguł lub języka specyficznego dla domeny (DSL) . Spójrz na te koncepcje, nie ma potrzeby wymyślania nowego koła.

Kilian Foth
źródło
16
Ale czy JavaScript nie byłby tutaj używany jako język skryptowy podobny do DSL? Konfigurujemy niezbędne dane (tylko do odczytu), zawijamy fragment kodu w funkcji i bezpiecznie go oceniamy. Ponieważ kod nie może nic zrobić oprócz zwrócenia wartości logicznej, nie byłoby tutaj żadnych złośliwych okazji.
amon
6
@Twittopher Przeciąganie całego silnika JavaScript w celu oceny niektórych predykatów wciąż wydaje się być 1) całkowitą nadwyżką, 2) ryzykowną i 3) podatną na błędy. W tym przypadku użyłeś ==zamiast ===w swoim przykładzie. Czy naprawdę chcesz zapewnić kompletność, gdy wszystkie reguły powinny być zawsze wygasane? Zamiast przeskakiwać między obręczami, aby upewnić się, że wszystkie interakcje między Javą a JavaScript są koszerne, dlaczego nie napisać prostego parsera i interpretera, jak sugerował Kilian? O wiele łatwiej będzie dostosować się do twoich potrzeb i zabezpieczyć. Użyj ANTLR lub czegoś takiego.
Doval
6
@Doval Pisanie niewielkiego DSL nie jest nauką o rakietach i mógłbym rozwinąć prosty język w ciągu 3 godzin do 5 dni. Ale wydaje się, że 1) całkowicie przesadzone, 2) ryzykowne i 3) podatne na błędy. Czy naprawdę chcesz napisać cały mini-język? Co zrobić, jeśli niektóre reguły biznesowe są bardziej skomplikowane niż oczekiwano i wymagają w pełni funkcjonalnego języka? Czy cierpisz na zespół niewymieniony tutaj? Zamiast odkrywać koło, dlaczego nie używasz istniejącego języka? O wiele łatwiej jest używać języka, który został już przetestowany w bitwie.
amon
14
@amon Kiedy to się stanie, znajdziesz prawdziwy silnik reguł (którym nie jest JavaScript), jak powiedział Killian. Zrównanie ryzyka obu podejść jest mylące. Wystarczy jedno pominięcie, aby zniszczyć wszystkie twoje wysiłki w celu zapewnienia tłumacza języka kompletnego; to proces subtraktywny. O wiele trudniej jest przypadkowo uczynić małą DSL niebezpieczną; to proces addytywny. Możliwym błędem, jaki możesz popełnić, jest niepoprawna interpretacja drzewa składni i może to zostać przetestowane jednostkowo. Prawdopodobnie nie da się przypadkowo nadać tłumaczowi formatu umiejętności dysku twardego.
Doval
4
@amon: Nawet jeśli fragment kodu js może nie mieć skutków ubocznych, może nie zwracać wartości boolowskiej:while (true) ;
Bergi
44

Zrobiłem to i nie polecam.

Napisałem całą logikę biznesową w Lua i zapisałem ten skrypt Lua w bazie danych. Gdy moja aplikacja się uruchomi, załaduje się i wykona skrypt. W ten sposób mogłem zaktualizować logikę biznesową mojej aplikacji bez dystrybucji nowego pliku binarnego.

Zawsze stwierdziłem, że zawsze muszę aktualizować plik binarny podczas wprowadzania zmian. Niektóre zmiany były w skrypcie Lua, ale niezmiennie mam listę zmian, które należało wprowadzić, więc prawie zawsze musiałem wprowadzać pewne zmiany w pliku binarnym i pewne zmiany w skrypcie Lua. Moja wyobraźnia, że ​​mogłabym cały czas unikać dystrybucji plików binarnych, po prostu się nie spełniła.

To, co uznałem za znacznie bardziej pomocne, było ułatwienie dystrybucji plików binarnych. Moja aplikacja automatycznie sprawdza dostępność aktualizacji podczas uruchamiania, pobiera i instaluje każdą aktualizację. Moi użytkownicy zawsze korzystają z najnowszych plików binarnych, które wypchnąłem. Nie ma prawie żadnej różnicy między zmianą pliku binarnego a zmianą skryptów. Gdybym to zrobił ponownie, włożyłbym jeszcze więcej wysiłku, aby aktualizacja była bezproblemowa.

Winston Ewert
źródło
3

Nie chciałbym, aby baza danych zawierała kod. Ale możesz zrobić coś podobnego, mając bazę danych zawierającą nazwy funkcji, a następnie używając refleksji, aby je wywołać. Kiedy dodajesz nowy warunek, musisz dodać go do kodu i bazy danych, ale możesz łączyć warunki i parametry, które są do nich przekazywane, aby tworzyć dość złożone oceny.

Innymi słowy, jeśli masz ponumerowane działy, łatwo byłoby sprawdzić czek UserDepartmentIs i TodayIsAfter, a następnie połączyć je, aby otrzymać Department = 2 i Today> 15/15/2016. Jeśli następnie chcesz sprawdzić TodayIsBefore, abyś mógł zakończyć datę uprawnienia, musisz napisać funkcję TodayIsBefore.

Nie zrobiłem tego dla uprawnień użytkownika, ale zrobiłem to dla sprawdzania poprawności danych, ale powinno działać.

jmoreno
źródło
2

XACML to rozwiązanie, którego naprawdę szukasz. Jest to rodzaj silnika reguł, który koncentruje się wyłącznie na kontroli dostępu. XACML, standard zdefiniowany przez OASIS, definiuje trzy części:

  • architektura
  • język zasad (który jest naprawdę tym, czego chcesz)
  • schemat zapytań / odpowiedzi (jak poprosić o decyzję o autoryzacji).

Architektura jest następująca:

  • Punkt decyzji politycznej (PDP) jest podstawową częścią architektury. Jest to składnik, który ocenia przychodzące żądania autoryzacji na podstawie znanego zestawu zasad
  • PEP (Policy Enforcement Point) to fragment kodu, który chroni twoją aplikację / API / usługę. PEP przechwytuje żądanie biznesowe, tworzy żądanie autoryzacji XACML, wysyła je do PDP, odbiera odpowiedź z powrotem i egzekwuje decyzję w odpowiedzi.
  • Punkt informacyjny polityki (PIP) to komponent, który może połączyć PDP z zewnętrznymi źródłami danych, np. LDAP, bazą danych lub usługą internetową. PIP przydaje się, gdy PEP wysyła zapytanie, np. „Czy Alice może wyświetlić dokument nr 12?” a PDP ma zasady, które wymagają wieku użytkownika. PDP zapyta PIP „daj mi wiek Alice”, a następnie będzie w stanie przetworzyć polisy.
  • PAP (Policy Administration Point) to miejsce, w którym zarządzasz całym rozwiązaniem XACML (definiowanie atrybutów, pisanie polityk i konfigurowanie PDP).

eXtensible Access Control Markup Language - architektura XACML

Oto jak wygląda Twój pierwszy przypadek użycia:

/*
 * All users in department 1 with over 5 years of seniority can VIEW resource FOOBAR-1, else not authorized
 * 
 */
 policy departmentOne{
    target clause department == 1
    apply firstApplicable
    /**
     * All users in department 1 with over 5 years of seniority can VIEW resource FOOBAR-1, else not authorized
     */
    rule allowFooBar1{
        target clause resourceId=="FOOBAR-1" and seniority>=5 and actionId=="VIEW"
        permit
    }
    rule denyOtherAccess{
        deny
    }

 }

Twój drugi przypadek użycia to:

 /*
  * "All users in department 2, if the date is after 03/15/2016, can VIEW resource FOOBAR-2, else not authorized"
  *  
  */
  policy departmentTwo{
    target clause department == 1
    apply firstApplicable
    rule allowFooBar2{
        target clause resourceId=="FOOBAR-1" and seniority>=5 and currentDate>"2016/03/15":date and actionId=="VIEW"
        permit
    }
    rule denyOtherAccess{
        deny
    }
  }

Możesz połączyć oba przypadki użycia w jedną zasadę, używając odniesień:

  policyset global{
    apply firstApplicable
    departmentOne
    departmentTwo
  }

I jesteś skończony!

Możesz przeczytać więcej o XACML i ALFA z:

David Brossard
źródło
0

To, czego naprawdę chcesz, to XACML . Daje to dokładnie to, czego chcesz. Niekoniecznie musisz wdrożyć pełną architekturę z całkowicie oddzielonymi rolami ... jeśli masz tylko jedną aplikację, prawdopodobnie uda Ci się zintegrować PDP i PEP z aplikacją za pomocą balana, a PIP jest czymkolwiek Twoja istniejąca baza danych użytkowników to.

Teraz, gdziekolwiek w aplikacji musisz coś autoryzować, tworzysz żądanie XACML, które zawiera użytkownika, akcję i kontekst, a silnik XACML podejmie decyzję na podstawie zapisanych plików zasad XACML. Te pliki zasad mogą być przechowywane w bazie danych lub w systemie plików lub w dowolnym miejscu, w którym chcesz zachować konfigurację. Axiomatics ma fajną alternatywę dla reprezentacji XML XACML o nazwie ALFA, która jest nieco łatwiejsza do odczytania niż nieprzetworzony XML, oraz wtyczkę Eclipse do generowania XACML XML na podstawie zasad ALFA.

gregsymons
źródło
1
Jak to odpowiada na zadane pytanie?
komar
W szczególności próbuje wdrożyć skonfigurowany zewnętrznie system autoryzacji. XACML jest gotowym do pracy zewnętrznie skonfigurowanym systemem autoryzacji, który bardzo dobrze opisuje jego konkretny przypadek użycia. Przyznaję, że może to nie być świetna odpowiedź na bardziej ogólne pytanie dotyczące dynamicznego wykonywania kodu, ale jest to dobre rozwiązanie dla jego konkretnego pytania.
gregsymons
0

Zrobiliśmy to w mojej obecnej firmie i jesteśmy bardzo zadowoleni z wyników.

Nasze wyrażenia są zapisane w js, a nawet używamy ich, aby ograniczyć wyniki uzyskiwane przez użytkowników z kwerendy ElasticSearch.

Sztuką jest upewnienie się, że dostępna jest wystarczająca ilość informacji, aby podjąć decyzję, abyś mógł naprawdę pisać dowolne perms bez zmian w kodzie, jednocześnie zachowując je szybko.

Naprawdę nie martwimy się atakami polegającymi na wstrzykiwaniu kodu, ponieważ uprawnienia są zapisywane przez tych, którzy nie muszą atakować systemu. To samo dotyczy ataków DOS, takich jak while(true)przykład. Administratorzy systemu nie muszą tego robić, mogą po prostu usunąć uprawnienia wszystkich ...

Aktualizacja:

Coś takiego jak XACML wydaje się być lepszym centralnym punktem zarządzania autoryzacją dla organizacji. Nasz przypadek użycia jest nieco inny, ponieważ nasi klienci zazwyczaj nie mają działu IT, który prowadziłby to wszystko. Potrzebowaliśmy czegoś samodzielnego, ale staraliśmy się zachować jak największą elastyczność.

Adagios
źródło