Choix de l'approche d'authentification pour l'application financière sur PostgreSQL

15

Tout d'abord quelques informations.

Le projet LedgerSMB est un projet de logiciel de comptabilité financière open source qui fonctionne sur PostgreSQL. Nous implémentons une très grande quantité de logique métier dans les fonctions définies par l'utilisateur, qui agissent comme le principal outil de mappage entre les méthodes d'objet programme et le comportement de la base de données. Actuellement, nous utilisons les utilisateurs de base de données comme utilisateurs d'authentification, en partie par choix (cela permet une logique de sécurité centralisée, de sorte que d'autres outils puissent être écrits et réutiliser les autorisations accordées aux utilisateurs), et en partie par nécessité (après avoir bifurqué depuis SQL-Ledger, il il n'y avait pas beaucoup d'options pour moderniser la sécurité sur cette base de code).

Cela nous permet d'accéder à un nombre raisonnable d'options de connexion unique auxquelles PostgreSQL a accès, de LDAP à Kerberos 5. Nous pouvons même utiliser PAM pour les mots de passe. Il nous permet également de réutiliser les autorisations lors de l'intégration avec d'autres applications, ou d'autoriser d'autres interfaces client. Pour une application de comptabilité financière, cela semble être une victoire nette.

Il y a des coûts évidents impliqués. Pour l'application Web, nous sommes très limités aux types d'authentification http qui peuvent être pris en charge. DIGEST par exemple est entièrement sorti. BASIC fonctionne, et nous pourrions implémenter KRB5 assez facilement (je prévois de le prendre en charge et de le sortir de la boîte pour 1.4). Des mesures d'authentification très fortes ne peuvent pas être correctement gérées directement sur ce point, bien que nous pourrions probablement les shimer si nécessaire (par exemple, BASIC + certificat SSL côté client avec un cn correspondant au nom d'utilisateur et une racine spécifique ca).

En même temps, nous avons rencontré pas mal de critiques, principalement de la part des développeurs et plus occasionnellement des dba qui me disent que l'application devrait être la barrière de sécurité, pas la base de données. À mon avis, un périmètre de sécurité plus petit est généralement meilleur, la réutilisation de la logique métier et de la logique de sécurité va de pair et il me semble dangereux de réutiliser la logique métier sans réutiliser la logique de sécurité au même niveau. du programme.

Suis-je en train de manquer des compromis majeurs ici? Y a-t-il des problèmes que je ne considère pas?

Chris Travers
la source
1
Transféré à la liste de diffusion pgsql-general. Voir le fil commençant ici .
Craig Ringer

Réponses:

17

Je pense que vous confondez authentification et autorisation .

Je suis tout à fait d'accord qu'il est sage de conserver le modèle de sécurité dans la base de données, d'autant plus que LedgerSMB est conçu avec un accès à partir de plusieurs clients à l'esprit. À moins que vous ne prévoyiez de passer à 3 niveaux avec une couche middleware, il est parfaitement logique d'avoir des utilisateurs comme rôles de base de données, en particulier pour quelque chose comme une application de comptabilité.

Cela ne signifie pas que vous devez authentifier les utilisateurs par rapport à la base de données à l'aide d'une méthode d'authentification prise en charge par PostgreSQL. Les utilisateurs, rôles et autorisations de votre base de données ne peuvent être utilisés pour l' autorisation que si vous le souhaitez.

Voici comment cela fonctionne pour une interface utilisateur Web par exemple:

  • janese connecte au serveur Web UI et s'authentifie à l'aide de la méthode souhaitée, par exemple la négociation du certificat client HTTPS X.509 et l'authentification DIGEST. Le serveur a maintenant une connexion d'un utilisateur qu'il accepte vraiment jane.

  • Le serveur se connecte à PostgreSQL en utilisant un nom d'utilisateur / mot de passe fixe (ou Kerberos ou tout ce que vous voulez), s'authentifiant lui-même sur le serveur db en tant qu'utilisateur webui. Le serveur db fait confiance webuipour authentifier ses utilisateurs et webuia donc reçu les GRANTs appropriés (voir ci-dessous).

  • Sur cette connexion, le serveur utilise SET ROLE jane;pour assumer le niveau d'autorisation de l'utilisateur jane. Jusqu'à ce RESET ROLE;qu'un ou un autre SET ROLEsoit exécuté, la connexion fonctionne avec les mêmes droits d'accès que janeet SELECT current_user()etc signalera jane.

  • Le serveur maintient l'association entre la connexion à la base de données sur laquelle il doit SET ROLEse connecter janeet la session Web pour l'utilisateur jane, ne permettant pas à cette connexion PostgreSQL d'être utilisée par d'autres connexions avec d'autres utilisateurs sans nouvel SET ROLEintermédiaire.

Vous vous authentifiez maintenant en dehors du serveur, mais vous conservez l' autorisation sur le serveur. Pg doit savoir quels utilisateurs existent, mais n'a pas besoin de mots de passe ou de méthodes d'authentification pour eux.

Voir:

Détails

Le serveur webui contrôle l'exécution des requêtes, et il ne laissera pas janes'exécuter le SQL brut (j'espère!) Donc janene peut pas RESET ROLE; SET ROLE special_admin_user;via l'interface utilisateur web. Pour plus de sécurité, j'ajouterais un filtre de déclaration au serveur qui a rejeté SET ROLEet à RESET ROLEmoins que la connexion ne soit établie ou n'entre dans un pool de connexions non attribuées.

Vous êtes toujours libre d'utiliser l'authentification directe auprès de Pg dans d'autres clients; vous pouvez mélanger et assortir librement. Vous avez juste à GRANTl' webuiutilisateur les droits sur SET ROLEles utilisateurs qui peuvent se connecter via le Web, puis donnez à ces utilisateurs les CONNECTdroits, mots de passe, etc. que vous souhaitez. Si vous souhaitez les rendre uniquement Web, REVOKEleurs CONNECTdroits sur la base de données (et à partir de public).

Pour faciliter une telle séparation authentification / autorisation, j'ai un rôle spécial assume_any_userauquel GRANTchaque utilisateur nouvellement créé doit jouer . J'ai ensuite GRANT assume_any_userle vrai nom d'utilisateur utilisé par des choses comme un frontal Web de confiance, leur donnant les droits de devenir n'importe quel utilisateur qu'ils aiment.

Il est important de créer assume_any_userun NOINHERITrôle, afin que l' webuiutilisateur ou quoi que ce soit n'a aucun privilège par lui-même et ne peut agir sur la base de données qu'une fois qu'il est SET ROLEdestiné à un utilisateur réel. Vous ne webuidevez en aucun cas être un superutilisateur ou un propriétaire de base de données .

Si vous regroupez des connexions, vous pouvez utiliser SET LOCAL ROLEpour définir le rôle uniquement dans une transaction, afin de pouvoir renvoyer des connexions au pool après COMMITou ROLLBACK. Attention, cela RESET ROLEfonctionne toujours, il n'est donc pas sûr de laisser le client exécuter le SQL qu'il veut.

SET SESSION AUTHORIZATIONest la version associée mais plus puissante de cette commande. Il ne nécessite pas d'appartenance à un rôle, mais c'est une commande uniquement superutilisateur. Vous ne voulez pas que votre interface utilisateur Web se connecte en tant que superutilisateur. Il peut être inversé avec RESET SESSION AUTHORIZATION, SET SESSION AUTHORIZATION DEFAULTou SET SESSION AUTHORIZATION theusernamepour reprendre les droits de super - utilisateur il est donc pas un privilège laissant tomber la barrière de sécurité soit.

Une commande qui fonctionnait comme SET SESSION AUTHORIZATIONmais qui était irréversible et qui fonctionnerait si vous étiez un membre du rôle mais pas un superutilisateur serait géniale. À ce stade, il n'y en a pas, mais vous pouvez toujours bien séparer l'authentification et l'autorisation si vous faites attention.

Exemple et explication

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;

Connectez-vous maintenant en tant que webui. Notez que vous ne pouvez rien faire, test_tablemais vous pouvez le SET ROLE faire jane, puis vous pouvez accéder à 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)

Notez que webui peut le SET ROLE faire jim, même lorsqu'il est déjà SET ROLEd janeet même s'il janen'a pas été GRANTautorisé à assumer le rôle jim. SET ROLEdéfinit votre ID utilisateur effectif, mais il ne supprime pas votre capacité à SET ROLEd'autres rôles, c'est une propriété du rôle que vous avez connecté en tant que, et non votre rôle effectif actuel. Par conséquent, vous devez contrôler soigneusement l'accès aux commandes SET ROLEet RESET ROLE. Il n'y a, AFAIK, aucun moyen d' SET ROLEétablir une connexion de manière permanente , en devenant vraiment l'utilisateur cible, même si ce serait certainement bien d'avoir.

Comparer:

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

à:

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

Cela signifie que ce SET ROLEn'est pas exactement la même chose que de se connecter en tant que rôle donné, quelque chose que vous devez garder à l'esprit.

webuine peut pas SET ROLEà dbownerpuisqu'il n'a pas été GRANTed ce droit:

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

donc en soi, il est assez impuissant, il ne peut assumer les droits des autres utilisateurs et que lorsque ces utilisateurs ont activé l'accès Web.

Craig Ringer
la source
1
btw vous voudrez peut-être voir comment pgbouncerfonctionne certains détails.
Craig Ringer
2
Oh, DISCARD ALLc'est une autre façon de ramener les droits par défaut. J'aimerais vraiment que Pg en ait un SET ROLE NORESETou similaire ...
Craig Ringer