Vous n'êtes pas identifié(e).
Pages : 1
Bonjour à tous
Je suis en train de développer une application IHM. A un certain moment j'offre à l'utilisateur la possibilité de changer son mot de passe de connexion à la bdd. Je lui donne donc un champ "ancien mot de passe" à saisir et deux champs "nouveau mot de passe". Ok je checke que les nouveaux mots de passe sont identiques et différents de l'ancien puis je checke que l'ancien mot de passe est bon puis je lance un alter role ... password ... pour modifier le mot de passe en bdd.
Sauf que je ne sais pas comment faire l'étape "checker ancien mot de passe". Comment vérifier que le mot de passe entré est bien celui stocké dans pg_authid? En plus c'est l'IHM qui fait le check, IHM qui possède les droits de l'utilisateur lequel n'a pas le droit de lire pg_authid. Et pg_roles ne donne pas les mots de passe.
Je pourrais tenter une nouvelle connexion avec ce mot de passe et checker si la connexion est/n'est pas acceptée mais d'une part je trouve ça lourd et d'autre part mon appli pourrait bien tourner dans des environnements qui ne nécessitent pas de mot de passe pour se connecter (methode "trust" dans pg_hba.conf).
Pour l'instant ma solution c'est quand l'utilisateur se connecte, je stocke le mot de passe qu'il saisit dans un attribut de l'objet qui gère la bdd. Ensuite quand il veut changer, je checke si le mot de passe qu'il saisit est le même que celui qui est enregistré. C'est pas très propre mais je ne vois pas d'autre solution.
C'est quand-même dommage qu'il n'y ait pas une requête/fonction toute faite type "select current_password" tout comme il y a "select current_user". Ou même un simple "select verify_password_is 'est_ce_bien_celui_ci'". Je ne vois même pas de souci de sécurité car cette requête ne concernerait que l'utilisateur connecté.
Voilà. Si quelqu'un sait comment monter cette requête...
Merci à tous.
Dernière modification par Svear (23/04/2021 08:10:15)
Hors ligne
Bonjour,
Je pense que votre approche pour l'authentification est problématique.
Votre application à priori se connecte à l'instance postgres avec le role fourni lors de la connexion plutôt qu'un role postgres dédié. C'est en général une mauvaise idée car cela pose plusieurs problèmes. Le plus important étant que cela rend votre application incompatible avec un pooler de connexion, ce qui revient à dire que votre application ne fonctionnera pas correctement avec de nombreux utilisateurs, mais cela pose également de potentiel problème de sécurité et/ou de mauvaise utilisation car vos utilisateurs ont techniquement un accès direct à l'instance. Implémenter à la place un système d'authentification externe résoud à la fois votre problème original ainsi que tous les problèmes qui découlent de l'utilisation d'un role postgres par utilisateur applicatif.
Julien.
https://rjuju.github.io/
En ligne
Super, merci de votre réponse
Désolé je débute dans ce forum.
Donc en ce qui concerne la connexion de l'appli avec le rôle fourni lors de la connexion, c'est exact. Toutefois ces rôles sont enregistrés dans postgres (je peux en rajouter ou en supprimer à volonté) mais seuls ces rôles peuvent s'y connecter.
Ensuite, l'utilisateur qui lance l'appli doit renseigner son identité + mot de passe, et ensuite l'appli effectue la connexion avec la bdd en fournissant ces informations.
Débutant en Postgres, j'ai quand-même tenté de faire les trucs bien en créant, dans la bdd elle-même, des roles dits "de groupe" (qui n'ont pas le droit login). Ces roles "de groupe" possèdent le droti "grant connect to". Ensuite les tables qui sont créées ont alors leurs droits insert/update/delete positionnés par rapport à ces roles "de groupe". Et les roles qui sont créés pour les utilisateurs sont ensuite "grantés" sur ces roles particuliers.
Voici par exemple comment est créé un nouveau role pour un utilisateur "toto" qui arrive
create role "toto" nosuperuser nocreatedb nocreaterole inherit login;
grant "sites_user" to "toto";
alter role "toto" password '123';
Le role "sites_user" étant donc ce role interne bdd, et ensuite tous les droits des tables sont gérés par rapport à ce "sites_user" dont "toto" hérite. Ensuite le monsieur "toto" arrive, je lui fournis "123" et il saisit "toto" + "123" dans les champs de l'appli et l'appli lui autorise ensuite l'accès (les menus se dégrisent, etc). En plus ça fonctionne bien quand "toto" se connecte, l'outil vérifie automatiquement les autorisations via postgres lui-même (requêtes "has_table_privilege()").
J'ai parfaitement conscience que cette méthode permettrait à toto de se connecter directement via psql (un petit linux portable qui usurperait l'IP des IP autorisées et hop) mais à quoi ça l'avancerait? Il n'aurait pas plus de droits via psql que ce qu'il en a avec l'appli. Les insert/update/delete qu'il ferait manuellement seraient les mêmes que ceux qui sont faits par l'appli et les logs Postgres enregistrairent ces insert/update/delete comme venant de "toto" comme il le fait quand ils viennent de l'appli avec "toto" qui y est connecté. Et "toto", qui est associé au role "sites_user", ne peut se connecter même en psql que sur la bdd pour laquelle "sites_user" possède le droit "connect". Question sécurité je pense être pas trop mal.
Mais effectivement mon appli n'est pas compatible avec un pooler de connexion (dont je connais la notion mais que je n'ai pas implémentée).
Mais à l'inverse, si l'appli se connectait avec un role dédié (que je présume donc directement créé avec la bdd), je ne pourrais plus gérer la connexion. Comment en effet distinguer "toto" de "titi"?
Dernière modification par Svear (23/04/2021 13:02:09)
Hors ligne
Débutant en Postgres, j'ai quand-même tenté de faire les trucs bien en créant, dans la bdd elle-même, des roles dits "de groupe" (qui n'ont pas le droit login). Ces roles "de groupe" possèdent le droti "grant connect to". Ensuite les tables qui sont créées ont alors leurs droits insert/update/delete positionnés par rapport à ces roles "de groupe". Et les roles qui sont créés pour les utilisateurs sont ensuite "grantés" sur ces roles particuliers.
[...]
Je suis d'accord que vous avez fait les choses bien, mais malheureusement cette approche pose de nombreux problèmes, et à mon avis vous aurez un retour de baton un jour ou l'autre si vous gardez cette approche.
J'ai parfaitement conscience que cette méthode permettrait à toto de se connecter directement via psql (un petit linux portable qui usurperait l'IP des IP autorisées et hop) mais à quoi ça l'avancerait? Il n'aurait pas plus de droits via psql que ce qu'il en a avec l'appli.
Et bien déjà il aurait le droit de changer son mot de passe sans devoir fournir le mot de passe actuel, ce qui est en contradiction avec ce que vous cherchez à faire. Il aurait également le droit d'ouvrir autant de connexions qu'il le souhaite, jusqu'à saturation, et donc empêcher tout le monde de se connecter. Ou exécuter des requêtes de son choix et consommer énormément de ressources, potentiellement jusqu'à saturation ou OOM, ou de créer ses propres objets, insérer en une seule requêtes des milliards de lignes et saturer l'espace disque, désactiver les traces de requêtes etc...
Mais effectivement mon appli n'est pas compatible avec un pooler de connexion (dont je connais la notion mais que je n'ai pas implémentée).
Mais à l'inverse, si l'appli se connectait avec un role dédié (que je présume donc directement créé avec la bdd), je ne pourrais plus gérer la connexion. Comment en effet distinguer "toto" de "titi"?
La distinction se ferait côté applicatif. Vous pouvez continuer à recevoir un utilisateur et mot de passe, et comparez ça avec une table locale contenant la liste des utilisateurs avec un secure hash du mot de passe. Votre applicatif retient alors l'utilisateur et autorise ou refuse les actions en fonction de son profil, également stocké dans des tables locales.
Julien.
https://rjuju.github.io/
En ligne
Je suis d'accord que vous avez fait les choses bien, mais malheureusement cette approche pose de nombreux problèmes, et à mon avis vous aurez un retour de baton un jour ou l'autre si vous gardez cette approche.
Hélas je n'ai pas assez de recul ni assez d'expérience. Mais je suis content que ce que j'ai fait ne soit déjà pas si mal.
Et bien déjà il aurait le droit de changer son mot de passe sans devoir fournir le mot de passe actuel, ce qui est en contradiction avec ce que vous cherchez à faire. Il aurait également le droit d'ouvrir autant de connexions qu'il le souhaite, jusqu'à saturation, et donc empêcher tout le monde de se connecter. Ou exécuter des requêtes de son choix et consommer énormément de ressources, potentiellement jusqu'à saturation ou OOM, ou de créer ses propres objets, insérer en une seule requêtes des milliards de lignes et saturer l'espace disque, désactiver les traces de requêtes etc...
D'accord, j'ai compris. Effectivement je n'ai vraiment pas assez d'expérience. Seul le changement de mot de passe en mode "sql" m'importe peu. Je ne vérifie pas le mot de passe pour l'embêter mais pour éviter qu'un idiot le lui change pendant qu'il va pissser. Si lui il veut le changer via la ligne de commande, ok pour moi.
Sinon effectivement tout un tas de possibilités auxquelles je n'ai pas pensé. Euh... il y a moyen de lui interdire de créer des objets via les grant? Et il peut vraiment désactiver les logs?
La distinction se ferait côté applicatif. Vous pouvez continuer à recevoir un utilisateur et mot de passe, et comparez ça avec une table locale contenant la liste des utilisateurs avec un secure hash du mot de passe. Votre applicatif retient alors l'utilisateur et autorise ou refuse les actions en fonction de son profil, également stocké dans des tables locales.
Pas mal du tout !!!
Merci de vos conseils, ils ont été précieux et considérés avec attention. D'autant plus que le projet en est à ses débuts donc je pose les bases ce qui veut dire que je peux les changer relativement facilement. Accessoirement (pour confirmation) il n'y a donc pas moyen de checker son propre mot de passe.
Hors ligne
D'accord, j'ai compris. Effectivement je n'ai vraiment pas assez d'expérience. Seul le changement de mot de passe en mode "sql" m'importe peu. Je ne vérifie pas le mot de passe pour l'embêter mais pour éviter qu'un idiot le lui change pendant qu'il va pissser. Si lui il veut le changer via la ligne de commande, ok pour moi.
Je suis entièrement d'accord, c'est une bonne chose de n'autoriser le changement de mot de passe qu'après la vérification du mot de passe actuel. Mais postgres ne fonctionne pas comme ça, du fait de sa gestion de l'authentification. Il n'y a aucune garantie qu'un role ait un mot de passe associé, et même s'il en a un il n'y aucune garantie que l'utilisateur ait eu besoin de fournir son mot de passe pour s'authentifier (ex: authentification trust ou certificat), ou qu'il ait fourni le mot de passe dont postgres à un jour eu connaissance (ex authentication pam ou kerberos/AD).
Sinon effectivement tout un tas de possibilités auxquelles je n'ai pas pensé. Euh... il y a moyen de lui interdire de créer des objets via les grant?
Oui évidemment. Et par défaut un utilisateur n'a le droit que de créer des objets dans le schéma public, c'est donc assez facile à configurer (et c'est d'ailleurs recommandé, cf https://wiki.postgresql.org/wiki/A_Guid … earch_Path ).
Et il peut vraiment désactiver les logs?
C'était un peu exagéré. Il pourra exécuter différents "SET config = valeur", mais au moins log_min_duration_statements et log_min_messages ne sont pas modifiables par un simple utilisateur. Il pourra cependant faire un
SET work_mem = '1TB';
SELECT * FROM pg_class c1 CROSS JOIN pg_class c2 CROSS JOIN pg_class c3...
et consommer énormément de mémoire. Vous pourriez essayer de mettre en place énormément de controles sur votre instance pour empêcher un utilisateur de faire n'importe quoi, mais de manière générale si vous voulez vous assurer de n'autoriser que les requêtes applicatives, il ne faut autoriser que votre applicatif à se connecter.
Merci de vos conseils, ils ont été précieux et considérés avec attention. D'autant plus que le projet en est à ses débuts donc je pose les bases ce qui veut dire que je peux les changer relativement facilement. Accessoirement (pour confirmation) il n'y a donc pas moyen de checker son propre mot de passe.
Postgres ne dispose pas d'une version "en clair" des mots de passe, pour raison de sécurité (cela était techniquement possible il y a quelques années en demandant de stocker explicitement un mot de passe en clair mais heureusement plus maintenant). Vous pourriez tenter de valider le mot de passe saisi avec le hash stocké en utilisant les même heuristiques que pour l'authentification, en supposant que l'utilisateur ait un mot de passe, mais cela supposerait d'écrire le code correspondant en C et de le packager dans une extension. Si vous utilisez vraiment une authentification par mot de passe, il est conseillé d'utiliser la méthode scram, et réimplémenter la validation est clairement loin d'être trivial
Julien.
https://rjuju.github.io/
En ligne
Oui évidemment. Et par défaut un utilisateur n'a le droit que de créer des objets dans le schéma public, c'est donc assez facile à configurer (et c'est d'ailleurs recommandé, cf https://wiki.postgresql.org/wiki/A_Guid … earch_Path ).
Ok, j'ai regardé. Il se trouve que j'avais déjà verrouillé. En effet, déjà d'une part, aucune de mes tables n'est créée dans le schéma public. Déjà je commence même par tout supprimer from "public" au schéma public. Pusi ensuite, je crée des schémas dédiés aux grands domaines de ma bdd puis toutes, absolument toutes les tables sont créées dans ces schémas. J'ai même pensé à virer le schéma public mais j'ai eu peur qu'il soit malgré tout nécessaire à Postgres lui-même
Et ensuite, pour chaque schéma,je fais un "revoke all privileges from public" puis un "grant usage on user_de_base".
Et donc (j'ai testé l'exemple de la page), un user qui se connecte ne peut pas créer d'objet/fonction, ni dans public, ni dans un des schémas. J'ai quand-même pas mal verrouillé le truc quoi.
mais de manière générale si vous voulez vous assurer de n'autoriser que les requêtes applicatives, il ne faut autoriser que votre applicatif à se connecter.
Oui, c'est logique. Merci de tous ces conseils, ils ont été précieux :applo:
Hors ligne
J'ai même pensé à virer le schéma public mais j'ai eu peur qu'il soit malgré tout nécessaire à Postgres lui-même
Non, il n'a aucune spécificité particulière (en dehors des droits pour PUBLIC). Mais supprimer le schéma public n'est pas le meilleur moyen d'améliorer la sécurité. Il est préférable de retirer les droits sur ce schéma.
Et ensuite, pour chaque schéma,je fais un "revoke all privileges from public" puis un "grant usage on user_de_base".
Inutile. Le schéma public est créé avec des droits spécifiques au groupe PUBLIC, mais les autres schémas créés par les utilisateurs n'ont pas de droit pour le groupe PUBLIC.
Guillaume.
Hors ligne
Merci à tous de vos précieux conseils. @Gleu ok un schéma créé n'a aucun droit public j'ai enregistré l'info mais tel le débutant maladroit qui met ses variables à 0 même s'il les réinitialise 3 lignes en dessous, je préfère laisser mon revoke all on ... from public. Ca me rassure (ceci dit ça me fait relativiser quand j'évolue dans des forums de prog et que je vois justement écrit "int i=0" alors qu'il y a un for (i=0; ...; i++) juste en dessous).
Donc j'ai appliqué les conseils de rjurju. J'ai créé une table des users avec leur mot de passe. Ensuite quand le user veut changer son mot de passe, je checke la signature dans la table avant d'accepter ou pas. Un peu de travail en plus par rapport à ma première idée mais je suis content
Merci à tous.
Dernière modification par Svear (27/04/2021 20:02:21)
Hors ligne
Salut
La sujet m’intéresse assez.
Donc j'ai appliqué les conseils de rjurju. J'ai créé une table des users avec leur mot de passe.
La table es-elle en local comme l'a suggéré rjuju?
Je me dit qu'avec un client en application desktop la mise à jour de la table locale peut être une casse-tête.
@+
Hors ligne
La table es-elle en local comme l'a suggéré rjuju?
Oui. J'avais justement un schema "admin" qui contenait des tables d'admin (comme par exemple une table qui gère la licence, la clef de chiffrement, etc). J'y ai rajouté une table "users" avec le login et le hash du mot de passe.
Je me dit qu'avec un client en application desktop la mise à jour de la table locale peut être une casse-tête.
Ben j'avais déjà un petit programme indépendant qui me permet de rajouter un utilisateur. Je lui ai rajouté un insert/update/delete dans cette table. Comme je suis seul à créer des users ça va. Effectivement si la mise à jour est dévolue à toute une équipe et que certains n'ont pas l'info il peut y avoir désynchronisation.
Hors ligne
Salut
Oui. J'avais justement un schema "admin" qui contenait des tables d'admin (comme par exemple une table qui gère la licence, la clef de chiffrement, etc). J'y ai rajouté une table "users" avec le login et le hash du mot de passe.
Donc la table est sur le serveur (dans la bd) et non sur le client (dans l'application cliente).
Or...
La distinction se ferait côté applicatif. Vous pouvez continuer à recevoir un utilisateur et mot de passe, et comparez ça avec une table locale contenant la liste des utilisateurs avec un secure hash du mot de passe. Votre applicatif retient alors l'utilisateur et autorise ou refuse les actions en fonction de son profil, également stocké dans des tables locales.
Donc pour lui la table des users en local signifie sur l'application cliente. Ce que je trouve vraiment hyper compliqué à mettre en œuvre (implanter toute la gestion des privilèges coté client!).
@+
Hors ligne
Salut
rjuju a écrit :La distinction se ferait côté applicatif. Vous pouvez continuer à recevoir un utilisateur et mot de passe, et comparez ça avec une table locale contenant la liste des utilisateurs avec un secure hash du mot de passe. Votre applicatif retient alors l'utilisateur et autorise ou refuse les actions en fonction de son profil, également stocké dans des tables locales.
Donc pour lui la table des users en local signifie sur l'application cliente. Ce que je trouve vraiment hyper compliqué à mettre en œuvre (implanter toute la gestion des privilèges coté client!).
@+
Oui, je parlais bien de tables dédiées sur l'instance postgres et d'une gestion externe de l'authentification et des autorisations par l'application. Cela ne devrait cependant pas être très compliqué à implémenter, et il s'agit de l'approche standard sur toutes les applications. Cela nécessite effectivement un peu plus d'efforts qu'exécuter des GRANT / REVOKE sur un ensemble de tables, mais si vous souhaitez des autorisations plus fine vous devriez mettre en place du ROW LEVEL SECURITY, et dans ce cas la gestion native n'est pas forcément triviale non plus.
Julien.
https://rjuju.github.io/
En ligne
Donc la table est sur le serveur (dans la bd) et non sur le client (dans l'application cliente).
Euh oui, exact La table est dans la bdd. Je ne savais pas qu'on pouvait implanter une table chez le client
Donc pour lui la table des users en local signifie sur l'application cliente. Ce que je trouve vraiment hyper compliqué à mettre en œuvre (implanter toute la gestion des privilèges coté client!).
Cela veut dire monter un second serveur Postgres chez le client ou alors j'ai rien pigé?
Enfin je suis un peu dépassé. Là j'ai une appli (qui est de type "portableApps"), je la dépose chez un client, il la lance, une fenêtre s'ouvre, il remplit les champs "ip", "port", "login", "mdp" et hop c'est connecté. Je ne vais pas lui faire installer un serveur Postgres...
Dernière modification par Svear (30/04/2021 09:43:57)
Hors ligne
J'ai l'impression qu'il y a un gros malentendu sur le vocabulaire. Une "table locale" ou "côté client" ici veut dire "implémenté avec le reste des objets SQL applicatifs créés sur la base de données", en opposition avec "utilisation de l'infrastructure existante dans postgres". Donc ici créer une table applicative pour gérer les utilisateurs plutôt qu'utiliser CREATE USER, créer une table applicative pour gérer les autorisation plutôt qu'utiliser GRANT / REVOKE (ou ROW LEVEL SECURITY ou autre) etc etc.
Julien.
https://rjuju.github.io/
En ligne
J'ai l'impression qu'il y a un gros malentendu sur le vocabulaire. Une "table locale" ou "côté client" ici veut dire "implémenté avec le reste des objets SQL applicatifs créés sur la base de données", en opposition avec "utilisation de l'infrastructure existante dans postgres". Donc ici créer une table applicative pour gérer les utilisateurs plutôt qu'utiliser CREATE USER, créer une table applicative pour gérer les autorisation plutôt qu'utiliser GRANT / REVOKE (ou ROW LEVEL SECURITY ou autre) etc etc.
Effectivement il y avait malentendu mais c'est la phrase de alassanediakite "Donc la table est sur le serveur (dans la bd) et non sur le client (dans l'application cliente)" qui m'a fait tiquer.
Donc dans ma bdd "truc" j'ai créé une table "admin"."users" qui contient mes users et leurs hash et c'est bien une table locale à l'instance Postgres de ma bdd. Et une autre bdd (pour un autre projet) aura elle-aussi sa propre table "admin"."users". Ca je l'avais bien pigé. Ainsi si je veux déplacer ma bdd ailleurs, je fais un simple pg_dump de ma bdd truc.
Dernière modification par Svear (30/04/2021 11:00:00)
Hors ligne
Salut
Dans ma compréhension...
Dans un environnement client/serveur: en local signifie sur la machine cliente.
Le terme "table locale" peut paraitre étrange en effet. J'ai compris que rjuju voulais dire que l'application cliente stocke (d'une manière ou d'une autre mais sécurisé) sur la machine cliente les infos (loggin+pwd+privilèges) des users.
Je pense que rjuju n'avait pas à ajouter le terme "locale" à "table" pour cette situation. Mais c'est déjà claire chez tout le monde
La solution adopté par svear pour verifier le mot de passe avant la modification est excellente à mon avis.
Mais pour ce qui est de laisser la gestion des privilèges avec l'application, client vaut mieux laisser le sgbd faire bien son travail sinon avec un projet moyen à plus (avec des sous domaines compta, facturation, paie...) ça peut devenir une vraies casse tête. voir mon projet www.logicoles.com
Je crois comprendre que la gestion des privilèges par l'application cliente est motivé par le prévision d'un gestionnaire de pool de connexions.
Je ne connais rein de ce concept à part qu'il aide PostgreSQL à dépasser une limitation de nombre de connexions. Si vous avez des tutos ou cours je suis preneur.
@+
Hors ligne
Le problème n'est pas de dépasser une limite du nombre de connexions (quoi que, cela peut aussi être le cas), mais surtout le fait qu'il est plus efficace d'effectuer un grand nombre de requêtes en utilisant un nombre plus restreint de connexions. Tout simplement parce que votre système, passé un certain nombre de processus, va commencer à prendre plus de temps à passer de l'exécution d'un processus à un autre qu'à traiter votre code applicatif. Et en bonus vous économiserez aussi sur le temps d'établissement des connexions si vous n'avez pas de connexion persistentes. Et si vous en avez, vous pourrez avoir moins de connexions "réelles", et économiser sur le surcout lié à un grand nombre de connexions, même si elles sont inactives.
Julien.
https://rjuju.github.io/
En ligne
Pages : 1