Les solutions SaaS multi-tenant reposent sur une base de données partagée pour optimiser coûts et maintenance, tout en desservant plusieurs clients aux exigences croissantes en matière de confidentialité et de conformité (RGPD, LPD). Sans isolation fine, tout incident peut entraîner une fuite de données entre tenants, une perte de confiance des utilisateurs et des sanctions légales. Edana accompagne les directions IT dans le design d’architectures cloud sécurisées, l’implémentation de bonnes pratiques de cybersécurité et l’optimisation des environnements PostgreSQL pour garantir performance, scalabilité et conformité réglementaire.
Modèles d’isolation multi-tenant
Chaque approche présente des compromis entre isolation, maintenance et performance. Le choix doit se fonder sur la volumétrie, la criticité des données et les contraintes réglementaires.
Schémas ou bases séparés
La séparation par schémas ou instances PostgreSQL offre une isolation maximale. Chaque client dispose de son propre espace, garantissant qu’aucune requête ne puisse franchir la limite imposée.
En pratique, cette approche est privilégiée pour les grands comptes ou les secteurs réglementés (banque, santé) où une obligation d’audit et de compartimentation stricte s’impose. Elle rassure les auditeurs et limite les risques de contamination croisée.
Cependant, multiplier les schémas ou instances complexifie les processus de migration et les opérations de patching. La coordination des versions, la synchronisation des scripts de migration et l’orchestration Kubernetes deviennent plus lourdes, ce qui peut freiner la vélocité des équipes DevOps. Cette approche s’intègre dans votre architecture web.
Colonne discriminante (tenant_id)
Insérer une colonne tenant_id dans chaque table paraît simple à mettre en œuvre. Le code applicatif inclut systématiquement un filtre pour isoler les données, évitant ainsi la surabondance de schémas.
Cependant, la responsabilité de la sécurisation repose entièrement sur l’application. Un oubli de clause WHERE peut exposer des données d’un autre client, générant une dette technique qui s’alourdit avec la complexité des requêtes analytiques et des rapports croisés.
De plus, l’analyse métier devient plus coûteuse : chaque requête de reporting doit gérer des agrégations multi-tenants, engendrant des temps de réponse variables selon le volume et l’indexation.
Row-Level Security (RLS)
Row-Level Security déplace l’isolation au niveau de la base de données. Les policies définies en SQL filtrent systématiquement les lignes accessibles par tenant, sans intervention du code applicatif.
La sécurité par défaut garantit que toute table activée pour RLS refuse l’accès si aucune policy n’est définie. Cela réduit drastiquement les risques liés à des oublis d’implémentation dans l’API ou le backend. Pour plus d’optimisation, consultez notre article sur l’optimisation des performances PostgreSQL.
Un client du secteur logistique a observé une réduction des incidents liés à l’exposition de données dès la mise en place de RLS. Cet exemple démontre qu’une sécurité native à PostgreSQL limite le risque opérationnel et renforce la confiance des utilisateurs.
Principe de RLS et configuration initiale
Row-Level Security s’active table par table et repose sur des policies SELECT, INSERT, UPDATE, DELETE. La mise en place centralisée renforce la robustesse et simplifie la maintenance.
Activation de RLS et création de policies
Pour chaque table concernée, il faut exécuter ALTER TABLE … ENABLE ROW LEVEL SECURITY. Cela active le pare-feu interne de PostgreSQL qui bloque toute requête avant de vérifier les policies définies.
Les policies sont créées via CREATE POLICY. Pour SELECT, on spécifie une clause USING qui détermine les lignes visibles. Pour INSERT ou UPDATE, une clause WITH CHECK valide que les nouvelles données respectent les critères de tenant.
En centralisant ces règles dans la base, l’équipe de développement évite la duplication de logique de filtrage dans chaque service ou micro-service, limitant la dette technique et homogénéisant les contrôles d’accès.
Comprendre USING et WITH CHECK
L’expression USING sert à restreindre les lignes retournées lors d’un SELECT. Par exemple, USING (tenant_id = current_setting(‘app.current_tenant’)::uuid) filtre automatiquement les données en fonction du contexte de session.
La clause WITH CHECK intervient sur les opérations d’INSERT ou UPDATE : elle refuse toute modification si la ligne insérée ne correspond pas au tenant courant. Ce double verrouillage assure l’intégrité métier sans surcharge applicative.
Un petit éditeur de logiciels a constaté qu’après l’ajout de WITH CHECK, aucun insert erroné n’a été détecté lors des phases de tests d’intégration. La visibilité native de PostgreSQL a permis d’identifier rapidement toute anomalie de contexte.
Responsabilité base de données vs applicative
Déplacer l’isolation au sein du SGBD sépare les préoccupations : la base gère la sécurité, l’application se concentre sur la logique métier et l’UI. Cela améliore la maintenabilité et la lisibilité du code.
En cas de refactor ou de nouvelle API, aucune adaptation de la logique de filtrage n’est nécessaire si les tables et policies sont déjà conçues. L’architecture gagne en agilité, et les erreurs humaines lors de codage de filtres sont éliminées.
Cela facilite également l’audit : les audits internes ou externes examinent directement les policies, sans devoir scruter chaque ligne de code applicatif, ce qui accélère la validation de conformité.
Prérequis et versions PostgreSQL
Row-Level Security est disponible depuis PostgreSQL 9.5. Il n’existe pas de dépendance à des extensions propriétaires, l’approche reste open source et compatible avec les éditions managées de cloud providers.
Aucun plugin externe n’est requis. Seule une version récente de PostgreSQL suffit, garantissant ainsi une portabilité aisée entre des clusters hébergés sur AKS, Azure Database for PostgreSQL ou Amazon RDS.
Cette compatibilité large prévient le vendor lock-in et facilite la migration entre environnements, en phase avec la vision Edana d’une infrastructure cloud flexible et évolutive.
Edana : partenaire digital stratégique en Suisse
Nous accompagnons les entreprises et les organisations dans leur transformation digitale
Transmission du contexte tenant via variables GUC
L’usage de custom GUC permet de transmettre l’ID du tenant à chaque session ou transaction. SET LOCAL garantit une remise à zéro automatique en fin de transaction.
Création et utilisation d’un custom GUC
PostgreSQL autorise la définition de paramètres utilisateurs via ALTER DATABASE … SET custom_variable_classes ou depuis postgresql.conf. On déclare app.current_tenant pour véhiculer l’UUID du client.
Pendant l’ouverture d’une session ou d’une transaction, un simple SET app.current_tenant = ‘…’ renseigne la variable. Les policies RLS lues via current_setting(…) comparent ensuite tenant_id à ce contexte.
La gestion de ce paramètre au niveau base simplifie la traçabilité et permet de garantir qu’aucune requête ne s’exécute sans contexte explicite de tenant.
Implémentation dans le code applicatif
Avant chaque transaction, l’application exécute SET LOCAL app.current_tenant = ‘uuid-du-client’. L’usage de SET LOCAL limite l’impact à la transaction courante.
En pratique, un middleware ou un hook sur l’ouverture de transaction dans SQLAlchemy ou tout autre ORM se charge d’injecter cette commande, assurant la cohérence quel que soit le chemin d’exécution.
Un prestataire de services financiers a automatisé cette étape dans son pool de connexions, réduisant les défaillances liées aux oublis de contexte et améliorant la fiabilité de bout en bout.
Importance de SET LOCAL et intégration avec PgBouncer
SET LOCAL remet la variable à sa valeur initiale à la fin de la transaction, évitant les fuites de contexte entre requêtes sur une même connexion.
Avec PgBouncer en mode transaction pooling, il convient de passer en session pooling ou d’étendre la logique de purge pour chaque transaction. Certains scripts de nettoyage peuvent être exécutés en hook pour réinitialiser le contexte.
Cette rigueur garantit que chaque requête perçoit uniquement les données du tenant actif, même en cas de forte réutilisation de connexions.
Mise en œuvre technique et exemples chiffrés
La configuration d’un rôle applicatif sans privilège BYPASSRLS assure que toutes les requêtes sont soumises aux policies. L’indexation systématique de tenant_id limite l’impact sur les performances.
Création d’un rôle applicatif sécurisé
On crée un rôle app avec NOLOGIN et sans privilège BYPASSRLS. Seules les fonctions ou les utilisateurs désignés disposent d’un droit étendu via SECURITY DEFINER ou grantees spécifiques.
Le rôle app exécute ensuite les opérations courantes : SELECT, INSERT, UPDATE, DELETE sur les tables RLS. Tout contournement nécessite explicitement le droit BYPASSRLS, ce qui simplifie l’audit des autorisations.
Ainsi, même en cas de faille applicative, la base de données refuse toute requête non conforme à la policy.
Schéma d’exemple : table assets
Imaginons une table assets(id UUID PRIMARY KEY, tenant_id UUID, name TEXT, metadata JSONB). Après ALTER TABLE assets ENABLE ROW LEVEL SECURITY, on crée :
CREATE POLICY assets_select ON assets FOR SELECT USING (tenant_id = current_setting(‘app.current_tenant’)::uuid);
CREATE POLICY assets_insert ON assets FOR INSERT WITH CHECK (tenant_id = current_setting(‘app.current_tenant’)::uuid);
GRANT SELECT, INSERT, UPDATE, DELETE ON assets TO app;
Indexation et optimisation
Un index B-tree sur tenant_id améliore drastiquement les performances des filtres RLS. Pour de très gros volumes, un index multicolonne tenant_id, created_at peut être envisagé pour les requêtes paginées.
Des tests de charge réalisés sur un cluster managé ont montré que l’ajout d’un index sur tenant_id réduisait le temps de requête de 30 % lors d’analyses multi-tenants sur plus de 100 millions de lignes.
Cet exemple illustre qu’une simple mesure d’indexation, combinée à RLS, garantit scalabilité et fluidité, en phase avec les besoins des applications SaaS.
Transformez l’isolation de vos données en atout stratégique
RLS sur PostgreSQL assure une isolation native, sécurisée par défaut et centralisée. En déportant la responsabilité des filtres au SGBD, la dette technique diminue et la conformité réglementaire est simplifiée. L’usage de custom GUC et de rôles restreints verrouille les accès tout en préservant la performance grâce à une indexation adaptée.
Des architectures cloud en Kubernetes, Azure ou AWS RDS se prêtent parfaitement à cette approche. Chaque projet reste contextualisé, fondé sur l’open source et modulable, fidèle à l’esprit Edana. Nos experts accompagnent l’audit de l’existant, la définition des policies, l’intégration continue des tests multi-tenant et la montée en charge opérationnelle.







Lectures: 1



