|
||||
Ouverture de sessionNavigationContactez-nousAdministration du site : RechercheSujets du forumSujets actifsNouveaux sujets:SyndicationSondageQuelle est la version de PostgreSQL la plus répandue sur vos serveurs ? 8.3 10% 8.2 42% 8.1 40% 8.0 2% 7.4 6% 7.3 ou antérieure 0% Nombre de votes: 48 |
Sécuriser votre base PostgreSQL| Sécuriser votre base PostgreSQLPar Guillaume Lelarge le 05/09/2007 - 17:04 Article écrit par Hubert Lubaczewski et traduit par Damien Clochard, le 18 août 2007. La version originale est disponible sur le blog de l'auteur où se trouvent beaucoup d'autres articles intéressants. Il y a quelques temps j'ai décrit comment « Je passerai par différentes étapes, en décrivant ce qui est possible, ce qui est simple et ce qui n'est pas si simple :) Commençons donc le tutoriel... Tout d'abord, je suppose que vous voulez protéger la base des mauvais agissements d'un utilisateur PostgreSQL. Je n'évoquerai pas la protection contre les utilisateurs système ou les administreurs. La première chose à faire consiste à interdire les connexions distantes pour les super-utilisateurs. C'est une modification basique qui se fait simplement. Cherchons tout d'abord le fichier pg_hba.conf. Sur mon système, il se trouve dans /home/pgdba/data/pg_hba.conf. Sur le vôtre, il est certainement ailleurs. Vous pouvez vérifier son emplacement en exécutant cette requête :
# show hba_file ; Bien. Maintenant, voici le contenu de ce fichier sur ma machine :
local all all trust Si vous ne comprenez pas ce que cela signifie, consultez le manuel. Maintenant, pour certaines raisons, j'ai trois comptes super-utilisateurs Voici le nouveau fichier pg_hba.conf :
local all @admins md5 Je viens également de créer un nouveau fichier, /home/pgdba/data/admins
depesz Remarque : N'oubliez pas qu'après chaque modification de votre fichier pg_hba.conf, vous devez redémarrer le serveur. Comme nous le voyons clairement, le fichier admins contient les noms des trois utilisateurs auxquels je souhaite limiter l'accès. Lorsque PostgreSQL vérifie le fichier pg_hab.conf, il s'arrête à la première ligne pertinente. Ainsi il est nécessaire de placer la ligne local all all trust après la ligne concernant le groupe d'administrateur ( local all @admins md5 ). Cependant, si l'on changeait l'ordre de la manière suivante :
local all all trust alors tous les utilisateurs seraient autorisés à se connecter sur n'importe quel compte PostgreSQL. Ceci serait regrettable donc soyez prudent en configurant le fichier pg_hba.conf Certaines personnes (notamment celles qui utilisent debian) sont fans de l'authentification ident sameuser. Les mots me manquent pour dire à quel point je déteste cela. Je ne vais pas rentrer dans les détails mais ce que je peux vous dire, c'est que j'utilise trust sur ma machine de développement (ordinateur portable) et uniquement md5 sur les serveurs de production. Je pense que l'authentification ident peut être utile dans certains cas, mais je n'ai pas trouvé lesquels. Pour le moment. Bref, la première étape était relatiment simple : nous avons protéger la base contre des utilisateurs inopportuns qui voudraient se connecter en tant que super-utilisateurs (à distance, tout du moins). Avant de poursuivre, évoquons les outils nommés dblink et dbilink. Souvenez-vous qu'utiliser ces modules abaissent la sécurité de votre système car ils modifient l'adresse IP depuis laquelle vous vous connectez. Je ne dis pas que ces modules sont mauvais : ils sont parfaitement sains, mais leur utilisation impose de réfléchir en préalable aux implications en terme de sécurité. Revenons maintenant à notre tutoriel. Nous avons notre base super top-secrète qui contient les orientations sexuelles de tout le monde. Nous ne voulons pas que n'importe quel utilisateur Postgres se connecte à la base à l'exception des utilisateurs dédiés (via une application web). Comment faire ? Revenons simplement au fichier pg_hba.conf et modifions-le :
local all @admins md5 Avec les lignes 2 et 3 (avec le mot 'secret'), j'autorise l'utisateur webapp à se connecter à la base secret en mode md5 et je rejette toutes les autres connections. Il est important de laisser local all @admins md5 en première ligne. En effet, si je plaçais cette ligne après la ligne local secret all reject, cela interdirait les connexions super-utilisateurs. Théoriquement, cette configuration semble correcte, mais vous devez procéder à deux configurations :
Pour ma part, je pense que laisser un accès aux super-utilisateurs est une bonne chose. Maintenant, lorsque je tente de me connecter avec un autre utilisateur (disons l'utilisateur test), la connexion est rejetée :
=> psql -U test -d secret Parfait. Personne ne peut se connecter à la base, à l'exception d'un utilisateur précis (webapp). Mais voici un problème : si quelqu'un obtenait l'accès à ce compte webapp, il/elle pourrait faire ce qu'il/elle veut. Supprimer des tables ? Pas de problème. Vider des tables ? Pas de problème. Mettre à jour ? insérer des données ? tout cela fonctionnera. Nous ne pouvons retirer ses droits au proprétaires des tables. Donc nous allons avoir besoin d'un utilisateur supplémentaire. Créons un utilisateur admin (qui ne serait pas super-utilisateur mais simplement administrateur de la base). L'utilisateur webapp sera dédié à l'application web et aura des droits restreints. Nous devons alors :
Cela semble simple. La commande create user est triviale. Le changement de propriétaire est fastidieux mais simple. Attribuer les droits d'administration requiert simplement d'ajouter la ligne suivant dans le fichier pg_hba.conf : local secret admin md5 avant la ligne : local secret all reject (et bien sûr redémarrer le serveur). Maintenant, passons à la partie revoke/grant :) Notre base a une table (et une sequence) :
> \d Rien de bien passionant, juste quelques données de test. Bien sûr, maintenant, webapp ne peut plus rien lire dans la table users :
(webapp@[local]:5830) 22:56:51 [secret] Maintenant, le problème est qu'un utilisateur malveillant peut (par exemple) créer une table et la remplir aléatoirement, juste pour planter PostgreSQL. La solution est simple :
Ce qui nous donne :
(admin@[local]:5830) 23:12:06 [secret] Il est nécessaire de se connecter à un compte super-utilisateur car le schéma public appartient aux super-utilisateurs et non pas au propriétaire de la base. OK. Résumons la situation :
Nous souhaitons maintenant que l'utilisateur webapp puisse lire les donnée. Nous pourrions faire quelque chose comme : grant select on table users to webapp; Mais ce n'est pas très cool. Les données de la table users sont très sensibles et nous ne voulons pas que l'utilisateur puisse éxecuter la commande : select * from users; et ainsi obtenir des informations sur la sexualité de millions de personnes. Nous voulons que l'utilisateur webapp soit capable de lire les donnée pour un utilisateur précis (les opérations INSERT, UPDATE et DELETE ne seront pas évoquées car la méthode est très similaire) Comment pouvons-nous réaliser cela ? Avec les procédures stockées bien sûr. Connectez-vous sur le compte admin et créez une fonction :
CREATE OR REPLACE FUNCTION get_user_record(in_user TEXT) RETURNS setof Cependant, puisque la fonction est écrite en plpgsql, elle est disponible par défaut pour tout le monde. Améliorons la sécurité :
(admin@[local]:5830) 23:24:21 [secret] Désormais notre utilisateur webapp peut appeler la fonction en donnant le nom exact qu'il recherche :
(webapp@[local]:5830) 23:26:10 [secret] mais il ne pourra pas obtenir tous les enregistrement de la table. Ce qui est plus important : pour que la fonction soit fonctionnelle, nous n'avons pas besoin de d'autoriser la selection (grant select) sur la table users pour l'utilisateur webapp. Ce tour de magie est opéré par la déclaration du gestionnaire de sécurité ( SECURITY DEFINER ) dans la définition de la fonction. Si vous n'êtes pas familier avec cette fonctionnalité, sachez qu'elle fonctionne plus ou moins comme suid sur les systèmes UNIX. Tous les utilisateurs appelant la fonction seront vus par PostgreSQL comme l'utilisateur qui a créé la fonction. Ce qui nous amène aux recommandation suivantes :
Dans notre situation, tout est bon : webapp ne pourra pas créer ses propres objets, ainsi il ne sera pas capable d'utiliser la fonction pour pirater la base, de plus current_user n'est pas réellement utile dans une base qui ne possède que deux utilisateurs :) Les fonctions "security definer" peuvent aussi définir des opérations INSERT/UPDATE/DELETE, qui feront les vérifications de paramètres nécessaires et exécuteront les actions requises, éventuellement acommpagnées par des traitements connexes, tel que le changement de login, la sauvegarde, la dénormalisation et ainsi de suite. Une dernière point à aborder : l'utilisateur webapp peut voir les noms des tables. Personnellement, cela ne me dérange pas, mais certaines personne peuvent souhaiter cacher les noms d'objets. Je n'ai pas vraiment creuser ce point, mais cela parait simple à faire. Cette fois, nous allons modifier les droits du schema pg_catalog :
(admin@[local]:5830) 23:36:17 [secret] (notez bien qu'il existe aussi un schéma d'informations, qui devra être modifié de la même façon, sinon il suffira d'interroger les tables et des vues du schéma information_schema pour obtenir les noms des objets !). Après ceci, l'utilisateur admin peut toujours travailler commme avant tandis que l'utilisateur webapp est contraint à certaines limitations :
(webapp@[local]:5830) 23:34:49 [secret] Comme vous pouvez le voir, l'utilisateur webapp ne peut pas voir la liste des tables, mais il est capable d'appeler la fonction get_user_record(). Par ailleurs, ce qui est important, c'est qu'il Il est possible dans certains cas que révoquer les droits du schéma pg_catalog provoque de mauvais résultats, mais (comme vous le constatez dans l'exemple ci-dessus) cela fonctionne et c'est assez simple à mettre en place. Récapitulons à nouveau ce que nous avons accompli :
Pour finir, voici quelques remarques : 1. En dehors des fonctions, les vues permettent également de limiter les droits d'accès : l'utilisateur n'a pas accès à la table mais a une vue batie sur celle-ci, ce qui permet de proposer une vue qui ne montre qu'une seule colonne de la table. Je n'en ai pas discuté ici car les vues n'ont pas la même flexibilité que les fonctions. De plus, dans les nouvelles versions de PostgreSQL, les fonctions sont presque aussi rapides que les vues. 2. Si vous avez déjà tout construit avec des fonctions, vous êtes prêt à utiliser l'outil pl/proxy pour effectuer de la répartition de charge (load-balancing). 3. Théoriquement on peut supposer qu'un utilisateur malveillant pourrait appeler la fonction get_user_record avec tous les noms possibles. C'est vrai, mais puisque nous pouvons écrire ce que nous voulons dans la fonction, il est assez simple de programmer la fonction afin qu'elle se bloque après le 3ème appel infructueux ou avertir l'administrateur par e-mail ou exécuter la commande pg_ctl stop pour empêcher immédiatement tout perte de données :) 4. Quand une base est protégée et en sécurité, souvenez-vous des sauvegardes. Je ne parle pas de sauvegarder la base. je parle de sauvegarder la base en toute sécurrité. Ne faites pas un simple dump de la base dans un fichier que vous entreposerez ailleurs. Chiffrer le fichier dump : au moins en utilisant "zip -e" au mieux avec gpg/pgp. |
|||
© PostgreSQLFr, tous droits réservés.
Site déclaré à la CNIL sous le numéro 1074678, conformément à la Loi en vigueur.