Wahl des Authentifizierungsansatzes für die Finanz-App unter PostgreSQL

15

Zunächst einige Hintergrundinformationen.

Das LedgerSMB-Projekt ist eine Open-Source-Finanzbuchhaltungssoftware, die auf PostgreSQL ausgeführt wird. Wir implementieren eine sehr große Menge an Geschäftslogik in benutzerdefinierten Funktionen, die als wichtigstes Mapping-Tool zwischen Programmobjektmethoden und Datenbankverhalten fungieren. Gegenwärtig verwenden wir Datenbankbenutzer als Authentifizierungsbenutzer, teils nach Wahl (dies ermöglicht eine zentralisierte Sicherheitslogik, damit andere Tools geschrieben und die den Benutzern erteilten Berechtigungen wiederverwendet werden können), teils nach Bedarf (nachdem wir uns von SQL-Ledger getrennt haben) Es gab nicht viele Möglichkeiten, die Sicherheit auf dieser Codebasis nachzurüsten.

Dies verschafft uns Zugang zu einer angemessenen Anzahl von Einzelanmeldungsoptionen, auf die PostgreSQL Zugriff hat, von LDAP bis Kerberos 5. Wir können sogar PAM verwenden, wenn es um Passwörter geht. Außerdem können wir Berechtigungen bei der Integration in andere Anwendungen oder beim Zulassen anderer Clientschnittstellen wiederverwenden. Für eine Finanzbuchhaltungsanwendung scheint dies ein Nettogewinn zu sein.

Es gibt offensichtliche Kosten. Für die Webanwendung sind wir stark auf die Arten von http-Authentifizierung beschränkt, die unterstützt werden können. DIGEST zum Beispiel ist komplett aus. BASIC funktioniert, und wir könnten KRB5 problemlos implementieren (ich plane, dies zu unterstützen und für 1.4 sofort einsatzbereit zu machen). Sehr starke Authentifizierungsmaßnahmen können auf diese Weise nicht direkt ordnungsgemäß verwaltet werden, obwohl wir sie möglicherweise bei Bedarf einbinden könnten (z. B. clientseitiges BASIC + -Zertifikat mit einer cn, die dem Benutzernamen entspricht, und einer bestimmten Stammzertifizierungsstelle).

Gleichzeitig sind wir auf eine Menge Kritik gestoßen, vor allem von Seiten der Entwickler und gelegentlich von dba's, die mir sagen, dass die Anwendung die Sicherheitsbarriere sein sollte, nicht die Datenbank. Ich bin immer noch der Ansicht, dass ein kleinerer Sicherheitsbereich im Allgemeinen besser ist, dass die Wiederverwendung von Geschäftslogik und Sicherheitslogik zusammengehört und dass die Wiederverwendung von Geschäftslogik ohne die Wiederverwendung von Sicherheitslogik auf derselben Ebene für mich gefährlich ist des Programms.

Vermisse ich hier irgendwelche großen Kompromisse? Gibt es Fallstricke, die ich nicht in Betracht ziehe?

Chris Travers
quelle
1
Cross-posted zur pgsql-general Mailingliste. Siehe den Thread, der hier beginnt .
Craig Ringer

Antworten:

17

Ich denke, Sie verschmelzen Authentifizierung und Autorisierung .

Ich stimme voll und ganz zu, dass es sinnvoll ist, das Sicherheitsmodell in der Datenbank beizubehalten, zumal LedgerSMB für den Zugriff mehrerer Clients konzipiert wurde. Sofern Sie nicht vorhaben, eine Middleware-Schicht in drei Ebenen einzurichten, ist es absolut sinnvoll, Benutzer als Datenbankrollen zu verwenden, insbesondere für eine Buchhaltungs-App.

Dies bedeutet nicht, dass Sie Benutzer mit einer von PostgreSQL unterstützten Authentifizierungsmethode gegenüber der Datenbank authentifizieren müssen. Ihre Datenbankbenutzer, Rollen und Berechtigungen können nur dann für die Autorisierung verwendet werden , wenn Sie dies möchten.

So funktioniert es beispielsweise für eine Web-Benutzeroberfläche:

  • janeStellt eine Verbindung zum Web-UI-Server her und authentifiziert sich mit einer beliebigen Methode, z. B. HTTPS X.509-Client-Zertifikat-Handshake und DIGEST-Authentifizierung. Der Server hat jetzt eine Verbindung von einem Benutzer, den er wirklich akzeptiert jane.

  • Der Server verbindet sich mit PostgreSQL mit einem festen Benutzernamen / Passwort (oder Kerberos oder was auch immer Sie möchten) und authentifiziert sich gegenüber dem Datenbankserver als Benutzer webui. Der db server vertraut darauf webui, seine Benutzer zu authentifizieren, so dass webuiihm die entsprechenden GRANTs zugewiesen wurden (siehe unten).

  • Bei dieser Verbindung SET ROLE jane;übernimmt der Server die Berechtigungsstufe des Benutzers jane. Bis der eine RESET ROLE;oder andere ausgeführt SET ROLEwird, arbeitet die Verbindung mit den gleichen Zugriffsrechten wie janeund wird SELECT current_user()usw. gemeldet jane.

  • Der Server behält die Zuordnung zwischen der Datenbankverbindung, auf der er sich befindet SET ROLE, janeund der Websitzung für den Benutzer bei janeund lässt nicht zu, dass diese PostgreSQL-Verbindung von anderen Verbindungen mit anderen Benutzern verwendet wird, ohne dass SET ROLEdazwischen eine neue Verbindung besteht .

Sie authentifizieren sich jetzt außerhalb des Servers, behalten jedoch die Autorisierung auf dem Server bei. Pg muss wissen, welche Benutzer vorhanden sind, benötigt jedoch keine Kennwörter oder Authentifizierungsmethoden.

Sehen:

Einzelheiten

Der Webui-Server steuert die Ausführung der Abfragen und lässt janeRaw-SQL nicht laufen (hoffe ich!), janeKann also nicht RESET ROLE; SET ROLE special_admin_user;über die Web-Benutzeroberfläche ausgeführt werden. Zusätzliche Sicherheit würde ich eine Erklärung Filter auf den Server hinzufügen , die abgelehnt SET ROLEund RESET ROLEes sei denn , die Verbindung war in oder einen Pool von nicht zugeordneten Verbindungen eingeben.

Es steht Ihnen weiterhin frei, die direkte Authentifizierung gegenüber PG in anderen Clients zu verwenden. Sie können frei mischen und zusammenpassen. Sie müssen GRANTdem webuiBenutzer lediglich die Rechte für SET ROLEBenutzer erteilen, die sich über das Web anmelden und diesen Benutzern dann die CONNECTgewünschten normalen Rechte, Kennwörter usw. erteilen können . Wenn Sie sie nur für das Web verwenden möchten, geben Sie REVOKEihre CONNECTRechte für die Datenbank (und von public) an.

Um eine solche Authentifizierung / Autorisierung möglichst einfach aufzuteilen, habe ich eine besondere Rolle assume_any_user, die ich GRANTjedem neu angelegten Benutzer zugedacht habe. Ich werde dann GRANT assume_any_userzu dem echten Benutzernamen, der von Dingen wie einem vertrauenswürdigen Web-Front-End verwendet wird, und gebe ihnen das Recht, jeder Benutzer zu werden, den sie mögen.

Es ist wichtig, assume_any_usereine NOINHERITRolle zu spielen, damit der webuiBenutzer oder was auch immer selbst keine Rechte hat und nur dann auf die Datenbank einwirken kann, wenn es sich SET ROLEum einen echten Benutzer handelt. Unter keinen Umständen sollte webuiein Superuser oder DB-Besitzer sein .

Wenn Sie ein Verbindungspooling durchführen, können Sie SET LOCAL ROLEdie Rolle nur innerhalb einer Transaktion festlegen, sodass Sie nach COMMIToder Verbindungen zum Pool zurückgeben können ROLLBACK. Beachten Sie, dass dies RESET ROLEimmer noch funktioniert. Es ist also immer noch nicht sicher, dass der Client das von ihm gewünschte SQL ausführt.

SET SESSION AUTHORIZATIONist die verwandte, aber stärkere Version dieses Befehls. Es ist keine Rollenmitgliedschaft erforderlich, aber es handelt sich um einen Superuser-Befehl. Sie möchten nicht, dass sich Ihre Webbenutzeroberfläche als Superuser verbindet. Es kann mit rückgängig gemacht werden RESET SESSION AUTHORIZATION, SET SESSION AUTHORIZATION DEFAULToder SET SESSION AUTHORIZATION theusernameSuperuser - Rechte wieder zu erlangen , so dass es entweder kein Privileg-dropping Sicherheitsbarriere ist.

Ein Befehl, der funktioniert hat, SET SESSION AUTHORIZATIONaber irreversibel ist und funktioniert, wenn Sie ein Rollenmitglied, aber kein Superuser sind, wäre großartig. Zu diesem Zeitpunkt gibt es noch keine, aber Sie können Authentifizierung und Autorisierung recht gut trennen, wenn Sie vorsichtig sind.

Beispiel und Erklärung

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;

Jetzt verbinden als webui. Beachten Sie, dass Sie nicht alles tun , um test_tableaber Sie können SET ROLE auf janeund dann können Sie zugreifen 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)

Beachten Sie, dass webui Dose SET ROLE zu jim, auch wenn schon SET ROLEd zu janeund obwohl janewird nicht GRANTdas Recht ed die Rolle zu übernehmen jim. SET ROLELegt Ihre effektive Benutzer-ID fest, jedoch nicht, dass Sie SET ROLEandere Rollen zuweisen können. Dies ist eine Eigenschaft der Rolle, mit der Sie verbunden sind, und nicht Ihre aktuelle effektive Rolle. Folglich müssen Sie den Zugriff auf die Befehle SET ROLEund sorgfältig kontrollieren RESET ROLE. Es gibt, AFAIK, keine Möglichkeit SET ROLEfür eine dauerhafte Verbindung, wirklich zum Zielbenutzer zu werden, obwohl es sicherlich schön wäre, eine zu haben.

Vergleichen Sie:

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

zu:

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

Dies bedeutet, dass SET ROLEdie Anmeldung nicht mit einer bestimmten Rolle identisch ist, was Sie berücksichtigen müssen.

webuikann nicht SET ROLE, dbownerda es nicht so GRANTrichtig bearbeitet wurde:

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

Daher ist es für sich genommen ziemlich machtlos. Es kann nur die Rechte anderer Benutzer übernehmen und nur, wenn für diese Benutzer der Webzugriff aktiviert ist.

Craig Ringer
quelle
1
Übrigens möchten Sie vielleicht nachsehen, wie dies pgbouncerfür einige Details funktioniert.
Craig Ringer
2
Oh, das DISCARD ALList eine andere Möglichkeit, um Rechte auf den Standard zurückzusetzen. Ich wünschte wirklich, Pg hätte eine SET ROLE NORESEToder ähnliche ...
Craig Ringer