Wybór metody uwierzytelniania dla aplikacji finansowej na PostgreSQL

15

Najpierw jakieś tło.

Projekt LedgerSMB to projekt oprogramowania księgowego o otwartym kodzie źródłowym, który działa na PostgreSQL. Implementujemy bardzo dużą logikę biznesową w funkcjach zdefiniowanych przez użytkownika, które działają jako główne narzędzie mapowania między metodami obiektów programu a zachowaniem bazy danych. Obecnie używamy użytkowników bazy danych jako użytkowników uwierzytelniających, częściowo z wyboru (pozwala to na scentralizowaną logikę bezpieczeństwa, dzięki czemu można pisać inne narzędzia i ponownie udzielać użytkownikom uprawnień), a częściowo z konieczności (po przejściu na SQL-Ledger, tam nie było wielu opcji modernizacji zabezpieczeń w tej bazie kodów).

To daje nam dostęp do rozsądnej liczby opcji pojedynczego logowania, do których ma dostęp PostgreSQL, od LDAP do Kerberos 5. Możemy nawet używać PAM, jeśli chodzi o hasła. Pozwala nam również ponownie wykorzystywać uprawnienia podczas integracji z innymi aplikacjami lub zezwalając na inne interfejsy klienta. W przypadku rachunkowości finansowej wydaje się to wygraną netto.

Wiąże się to z oczywistymi kosztami. W przypadku aplikacji internetowej jesteśmy bardzo ograniczeni do typów uwierzytelniania HTTP, które mogą być obsługiwane. Na przykład DIGEST jest całkowicie niedostępny. BASIC działa, a my moglibyśmy zaimplementować KRB5 dość łatwo (planuję mieć to obsługiwane i działać od razu z wersji 1.4). Bardzo silnymi środkami uwierzytelniania nie można właściwie zarządzać bezpośrednio na tym, chociaż prawdopodobnie moglibyśmy je w razie potrzeby pomijać (na przykład certyfikat SSL po stronie klienta BASIC + z cn pasującym do nazwy użytkownika i określonego katalogu głównego).

W tym samym czasie spotkałem się z dużą krytyką głównie ze strony tłumaczy programistów, a sporadycznie od dba, którzy twierdzą, że aplikacja powinna stanowić barierę bezpieczeństwa, a nie bazę danych. Nadal uważam, że mniejszy obwód bezpieczeństwa jest ogólnie lepszy, że ponowne użycie logiki biznesowej i logiki bezpieczeństwa idzie w parze i że wydaje mi się niebezpieczne ponowne użycie logiki biznesowej bez ponownego użycia logiki bezpieczeństwa na tym samym poziomie programu.

Czy brakuje mi tutaj poważnych kompromisów? Czy są jakieś braki, których nie rozważam?

Chris Travers
źródło
1
Przeniesiony do ogólnej listy mailingowej pgsql. Zobacz wątek rozpoczynający się tutaj .
Craig Ringer

Odpowiedzi:

17

Myślę, że łączysz uwierzytelnianie i autoryzację .

Całkowicie się zgadzam, że zachowanie modelu bezpieczeństwa w DB jest mądre, zwłaszcza, że ​​LedgerSMB został zaprojektowany z myślą o dostępie wielu klientów. Jeśli nie planujesz przejścia 3-warstwowego z warstwą oprogramowania pośredniego, będzie to idealne rozwiązanie sens mieć użytkownikom jak role baz danych, zwłaszcza na coś aplikacji księgowych.

Ten sposób nie oznacza, że musisz uwierzytelniać użytkowników przeciwko bazy danych przy użyciu metody uwierzytelniania PostgreSQL obsługiwane. Użytkownicy bazy danych, role i granty mogą być używane do autoryzacji tylko, jeśli chcesz.

Oto jak to działa na przykład w interfejsie internetowym:

  • janełączy się z serwerem interfejsu WWW i uwierzytelnia się za pomocą dowolnej metody, powiedzmy handshake certyfikatu klienta HTTPS X.509 i autoryzację DIGEST. Serwer ma teraz połączenie od użytkownika, który akceptuje jane.

  • Serwer łączy się z PostgreSQL przy użyciu stałej nazwy użytkownika / hasła (lub Kerberos lub cokolwiek innego), uwierzytelniając się na serwerze db jako użytkownik webui. Serwer db ufa webuiuwierzytelnianiu swoich użytkowników, dlatego webuiotrzymał odpowiednie GRANTs (patrz poniżej).

  • W przypadku tego połączenia serwer SET ROLE jane;przyjmuje poziom autoryzacji użytkownika jane. Dopóki RESET ROLE;lub inny SET ROLEjest prowadzony, połączenie działa z tych samych praw dostępu, jak janei SELECT current_user()etc zgłosi jane.

  • Serwer utrzymuje powiązanie między połączeniem bazy danych, z którym musi SET ROLEsię połączyć, janea sesją internetową dla użytkownika jane, nie pozwalając, aby to połączenie PostgreSQL było używane przez inne połączenia z innymi użytkownikami bez nowej SET ROLEprzerwy między nimi.

Teraz uwierzytelniasz się poza serwerem, ale zachowujesz autoryzację na serwerze. Pg musi wiedzieć, którzy użytkownicy istnieją, ale nie potrzebuje dla nich haseł ani metod uwierzytelniania.

Widzieć:

Detale

Serwer webui kontroluje uruchamianie zapytań i nie pozwoli na janeuruchomienie surowego SQL (mam nadzieję!), Więc janenie może RESET ROLE; SET ROLE special_admin_user;za pośrednictwem interfejsu WWW. Dla większego bezpieczeństwa dodam filtr instrukcji do serwera, który odrzucił SET ROLEi RESET ROLEchyba, że ​​połączenie było w puli nieprzypisanych połączeń lub nie wchodziło do niej.

Nadal możesz swobodnie korzystać z bezpośredniego uwierzytelniania Pg na innych klientach; możesz dowolnie mieszać i dopasowywać. Po prostu trzeba GRANTna webuiużytkownika prawa do SET ROLEużytkowników, którzy mogą logować się za pośrednictwem Internetu, a następnie dać tym użytkownikom żadnych normalnych CONNECTpraw, haseł, itp chcesz. Jeśli chcesz, aby były dostępne tylko w Internecie, REVOKEich CONNECTprawa do bazy danych (i od public).

Aby ułatwić podział uwierzytelnienia / autoryzacji, mam szczególną rolę assume_any_user, do której GRANTkażdy nowo utworzony użytkownik. Ja wtedyGRANT assume_any_user przechodzę do prawdziwej nazwy użytkownika używanej przez takie rzeczy, jak zaufany interfejs internetowy, dając im prawa do zostania dowolnym użytkownikiem, którego lubią.

Ważne jest, aby assume_any_userna NOINHERITrolę, więc webuiużytkownik lub cokolwiek ma privilges przez jego własny i może działać tylko w bazie danych po to SET ROLEdo prawdziwego użytkownika. Pod żadnym pozorem nie powinien webuibyć superużytkownikiem ani właścicielem bazy danych .

Jeśli korzystasz z puli połączeń, możesz użyć, SET LOCAL ROLEaby ustawić rolę tylko w ramach transakcji, abyś mógł zwracać połączenia do puli po COMMITlub ROLLBACK. Strzeż się, że RESET ROLEnadal działa, więc nadal nie jest bezpieczne, aby klient mógł uruchamiać dowolny SQL, jaki chcą.

SET SESSION AUTHORIZATIONjest powiązaną, ale silniejszą wersją tego polecenia. Nie wymaga przynależności do roli, ale jest to tylko polecenie administratora. Nie chcesz, aby interfejs użytkownika sieci Web był łączony jako administrator. To może być odwrócone RESET SESSION AUTHORIZATION, SET SESSION AUTHORIZATION DEFAULTlub SET SESSION AUTHORIZATION theusernameodzyskać praw superużytkownika, więc nie jest to bariera bezpieczeństwa przywilej opada albo.

Polecenie, które działało jak SET SESSION AUTHORIZATION ale było nieodwracalne i działałoby, gdybyś był członkiem roli, ale nie superużytkownikiem, byłoby świetne. W tym momencie nie ma jednego, ale nadal możesz całkiem dobrze rozdzielić uwierzytelnianie i autoryzację, jeśli jesteś ostrożny.

Przykład i wyjaśnienie

CREATE ROLE dbowner NOLOGIN;
CREATE TABLE test_table(x text);
INSERT INTO test_table(x) VALUES ('bork');
ALTER TABLE test_table OWNER TO dbowner;

CREATE ROLE assume_any_user NOINHERIT NOLOGIN;
CREATE ROLE webui LOGIN PASSWORD 'somepw' IN ROLE assume_any_user;

CREATE ROLE jane LOGIN PASSWORD 'somepw';
GRANT jane TO assume_any_user;
GRANT ALL ON TABLE test_table TO jane;

CREATE ROLE jim LOGIN PASSWORD 'somepw';
GRANT jim TO assume_any_user;

Teraz połącz jako webui. Należy pamiętać, że nie można nic zrobić, test_tableale może SET ROLE się janei wtedy można uzyskać dostęp test_table:

$ psql -h 127.0.0.1 -U webui regress
Password for user webui:

regress=> SELECT session_user, current_user;
 session_user | current_user 
--------------+--------------
 webui        | webui
(1 row)



regress=> SELECT * FROM test_table;
ERROR:  permission denied for relation test_table

regress=> SET ROLE jane;
SET

regress=> SELECT session_user, current_user;
 session_user | current_user 
--------------+--------------
 webui        | jane
(1 row)

regress=> SELECT * FROM test_table;
  x   
------
 bork
(1 row)

Należy pamiętać, że webui puszka SET ROLE do jim, nawet gdy już SET ROLEdni do janei chociaż janenie został GRANTed prawo przyjąć rolę jim. SET ROLEustawia efektywny identyfikator użytkownika, ale nie usuwa twojej zdolności do SET ROLEpełnienia innych ról, jest to właściwość roli, którą podłączyłeś, a nie twojej obecnej skutecznej roli. W związku z tym musisz dokładnie kontrolować dostęp do poleceń SET ROLEi RESET ROLE. Nie ma, AFAIK, żadnego sposobu na stałe SET ROLEpołączenie, naprawdę stając się docelowym użytkownikiem, choć na pewno byłoby miło mieć.

Porównać:

$ psql -h 127.0.0.1 -U webui regress
Password for user webui:

regress=> SET ROLE jane;
SET

regress=> SET ROLE jim;
SET
regress=> SELECT session_user, current_user;
 session_user | current_user 
--------------+--------------
 webui        | jim
(1 row)

do:

$ psql -h 127.0.0.1 -U jane regress
Password for user jane:

regress=> SET ROLE webui;
ERROR:  permission denied to set role "webui"
regress=> SET ROLE jim;
ERROR:  permission denied to set role "jim"

Oznacza to, że SET ROLEnie jest to dokładnie to samo, co logowanie się w ramach określonej roli, o czym należy pamiętać.

webuinie mogę tego SET ROLEzrobić, dbownerponieważ nie zostało GRANTto odpowiednio poprawione:

regress=> SET ROLE dbowner;
ERROR:  permission denied to set role "dbowner"

więc sam w sobie jest dość bezsilny, może przejąć prawa innych użytkowników i tylko wtedy, gdy ci użytkownicy mają włączony dostęp do sieci.

Craig Ringer
źródło
1
btw możesz chcieć zobaczyć, jak pgbouncerdziała dla niektórych szczegółów.
Craig Ringer
2
Och, DISCARD ALLto kolejny sposób na przywrócenie praw do wartości domyślnych. Naprawdę chciałbym, żeby Pg miał coś SET ROLE NORESETpodobnego ...
Craig Ringer