Scelta dell'approccio di autenticazione per l'app finanziaria su PostgreSQL


15

Prima alcuni retroscena.

Il progetto LedgerSMB è un progetto software di contabilità finanziaria open source che gira su PostgreSQL. Implementiamo una grande quantità di logica aziendale nelle funzioni definite dall'utente, che fungono da strumento principale di mappatura tra i metodi degli oggetti del programma e il comportamento del database. Attualmente utilizziamo gli utenti di database come utenti di autenticazione, in parte per scelta (ciò consente una logica di sicurezza centralizzata, in modo che altri strumenti possano essere scritti e riutilizzare le autorizzazioni fornite agli utenti), e in parte per necessità (dopo che abbiamo biforcato da SQL-Ledger, lì non c'erano molte opzioni per il retrofit della sicurezza su quella base di codice).

Questo ci consente di accedere a un numero ragionevole di opzioni di accesso singolo a cui PostgreSQL ha accesso, da LDAP a Kerberos 5. Possiamo persino usare PAM per quanto riguarda le password. Ci consente inoltre di riutilizzare le autorizzazioni quando ci si integra con altre applicazioni o si consentono altre interfacce client. Per un'applicazione di contabilità finanziaria questa sembra una vittoria netta.

Ci sono costi ovvi coinvolti. Per l'applicazione Web, siamo molto limitati ai tipi di http http che possono essere supportati. DIGEST per esempio è completamente uscito. BASIC funziona e potremmo implementare KRB5 abbastanza facilmente (ho intenzione di avere questo supportato e lavorare fuori dalla scatola per 1.4). Misure di autenticazione molto forti non possono essere gestite correttamente su questo direttamente, anche se probabilmente potremmo inserirle in caso di necessità (ad esempio cert SSL BASIC + lato client con un cn corrispondente al nome utente e una radice ca specifica).

Allo stesso tempo ci siamo imbattuti in una buona dose di critiche principalmente dalla folla dello sviluppo e più occasionalmente dai dba che mi dicono che l'applicazione dovrebbe essere la barriera di sicurezza, non il database. La mia opinione è ancora che un perimetro di sicurezza più piccolo sia generalmente migliore, che il riutilizzo della logica aziendale e della logica di sicurezza vadano di pari passo e che mi sembri pericoloso riutilizzare la logica aziendale senza riutilizzare la logica di sicurezza allo stesso livello del programma.

Mi sto perdendo importanti compromessi qui? Ci sono dei gotcha che non sto prendendo in considerazione?


1
Pubblicazione incrociata nella mailing list pgsql-general. Vedi il thread che inizia qui .
Craig Ringer,

Risposte:


17

Penso che stai unendo autenticazione e autorizzazione .

Sono pienamente d'accordo sul fatto che mantenere il modello di sicurezza nel DB sia saggio, soprattutto perché LedgerSMB è progettato pensando all'accesso da più client. A meno che tu non abbia intenzione di passare a 3 livelli con un livello middleware, ha perfettamente senso avere utenti come ruoli di database, specialmente per qualcosa come un'app di contabilità.

Ciò non significa che devi autenticare gli utenti sul database usando un metodo di autenticazione supportato da PostgreSQL. Gli utenti, i ruoli e le sovvenzioni del database possono essere utilizzati per l' autorizzazione solo se lo si desidera.

Ecco come funziona per un'interfaccia utente Web, ad esempio:

  • janesi collega al server dell'interfaccia utente Web e si autentica utilizzando il metodo desiderato, ad esempio handshake del certificato client HTTPS X.509 e autenticazione DIGEST. Il server ora ha una connessione da un utente che accetta davvero jane.

  • Il server si connette a PostgreSQL usando un nome utente / password fissi (o Kerberos o qualunque cosa tu voglia), autenticandosi sul server db come utente webui. Il server db si fida webuidell'autenticazione dei suoi utenti, quindi webuisono stati dati i messaggi appropriati GRANT(vedi sotto).

  • Su quella connessione il server utilizza SET ROLE jane;per assumere il livello di autorizzazione dell'utente jane. Fino a quando RESET ROLE;o in un altro SET ROLEviene eseguito, il collegamento è in funzione con gli stessi diritti di accesso come janee SELECT current_user()ecc riporterà jane.

  • Il server mantiene l'associazione tra la connessione al database su cui si ha SET ROLEa janee la sessione web per l'utente jane, non permettere che la connessione PostgreSQL per essere utilizzati da altre connessioni con altri utenti senza un nuovo SET ROLEmezzo.

Ora stai eseguendo l' autenticazione all'esterno del server, ma mantenendo l' autorizzazione nel server. Pg ha bisogno di sapere quali utenti esistono, ma non ha bisogno di password o metodi di autenticazione per loro.

Vedere:

Dettagli

Il server webui controlla l'esecuzione delle query e non consentirà l' janeesecuzione di SQL raw (spero!), Quindi janenon è possibile RESET ROLE; SET ROLE special_admin_user;tramite l'interfaccia utente Web. Per maggiore sicurezza, aggiungerei un filtro di istruzioni al server che ha rifiutato SET ROLEe, a RESET ROLEmeno che la connessione non fosse o entrasse in un pool di connessioni non assegnate.

Sei ancora libero di utilizzare l'autenticazione diretta su Pg in altri client; puoi mescolare e abbinare liberamente. Non vi resta che GRANTl' webuiutente i diritti per SET ROLEgli utenti che possono accedere via web e poi dare quegli utenti eventuali normali CONNECTdiritti, password, ecc che si desidera. Se vuoi renderli solo web, i REVOKEloro CONNECTdiritti sul database (e da public).

Per semplificare una tale suddivisione dell'autenticazione / autorizzazione, ho un ruolo speciale a assume_any_usercui GRANTogni utente appena creato. Passo quindi GRANT assume_any_useral vero nome utente utilizzato da cose come un front-end web di fiducia, dando loro il diritto di diventare qualsiasi utente a loro piace.

È importante ricoprire assume_any_userun NOINHERITruolo, quindi l' webuiutente o qualsiasi altra cosa non ha alcun privilegio da solo e può agire sul database solo una volta che è SET ROLEun utente reale. In nessun caso dovrebbe webuiessere un superutente o un proprietario di DB .

Se si utilizza il pool di connessioni, è possibile utilizzare SET LOCAL ROLEper impostare il ruolo solo all'interno di una transazione, in modo da poter restituire le connessioni al pool dopo COMMITo ROLLBACK. Attenzione che RESET ROLEfunziona ancora, quindi non è ancora sicuro lasciare che il client esegua qualunque SQL voglia.

SET SESSION AUTHORIZATIONè la versione correlata ma più forte di questo comando. Non richiede l'appartenenza al ruolo, ma è solo un comando per superutente. Non vuoi che la tua interfaccia web si connetta come superutente. Si può essere invertito con RESET SESSION AUTHORIZATION, SET SESSION AUTHORIZATION DEFAULTo SET SESSION AUTHORIZATION theusernameper recuperare i diritti di superutente quindi non è una barriera di sicurezza privilegio far cadere neanche.

Un comando che funzionava come SET SESSION AUTHORIZATIONse fosse irreversibile e funzionerebbe se fossi un membro del ruolo ma non un superutente sarebbe fantastico. A questo punto non ce n'è uno, ma puoi comunque separare abbastanza bene l'autenticazione e l'autorizzazione se stai attento.

Esempio e spiegazione

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;

Ora connettiti come webui. Nota che non puoi fare nulla, test_tablema puoi SET ROLE farlo janee quindi puoi accedere a 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)

Si noti che webui può SET ROLE a jim, anche quando già SET ROLEÐ janee anche se janenon è stato GRANTcato il diritto di assumere il ruolo jim. SET ROLEimposta il tuo ID utente effettivo, ma non rimuove la tua capacità di SET ROLEricoprire altri ruoli, questa è una proprietà del ruolo in cui ti sei connesso, non del tuo attuale ruolo effettivo. Di conseguenza è necessario controllare attentamente l'accesso ai comandi SET ROLEe RESET ROLE. AFAIK non esiste un modo per stabilire SET ROLEuna connessione in modo permanente , diventando veramente l'utente di destinazione, anche se sarebbe sicuramente bello averlo.

Confrontare:

$ 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)

per:

$ 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"

Ciò significa che SET ROLEnon è esattamente uguale all'accesso come ruolo dato, qualcosa che devi tenere a mente.

webuinon può SET ROLEper dbownerquanto non è stato GRANTcato tale diritto:

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

quindi da solo è piuttosto impotente, può assumere solo i diritti di altri utenti e solo quando tali utenti hanno l'accesso al web abilitato.


1
tra l'altro potresti voler vedere come pgbouncerfunziona per alcuni dettagli.
Craig Ringer,

2
Oh, DISCARD ALLè un altro modo per riportare i diritti ai valori predefiniti. Vorrei davvero che Pg ne avesse avuto uno SET ROLE NORESETsimile ...
Craig Ringer,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.