Dans le paysage SaaS moderne, la multi-location n'est pas seulement une stratégie de déploiement ; c'est une exigence architecturale fondamentale. Bien que les bases de données partagées offrent des économies de coûts significatives et des sauvegardes simplifiées, elles introduisent un défi de sécurité critique : l'isolation des données. Comment vous assurer que le Locataire A ne voit jamais les données du Locataire B, même en cas de bug de l'application ou d'erreur du développeur ? La réponse réside dans la sécurité au niveau des lignes (RLS).
La sécurité au niveau des lignes est une fonctionnalité puissante disponible dans les bases de données relationnelles avancées telles que PostgreSQL, Azure SQL et Oracle. Elle permet au moteur de base de données lui-même d'appliquer des politiques de contrôle d'accès, déplaçant ainsi la charge de la sécurité de la couche application vers la couche données. Dans cet article, nous explorerons comment implémenter efficacement la RLS, garantissant une isolation robuste sans sacrifier les performances ou l'expérience développeur.
Pourquoi la RLS bat le filtrage au niveau de l'application
L'approche traditionnelle de la multi-location consiste à ajouter une colonne tenant_id à chaque table et à s'assurer que chaque requête inclut une clause WHERE tenant_id = current_user_tenant_id. Bien que cela fonctionne, cela repose entièrement sur l'exactitude du code de l'application. Si un développeur oublie le filtre, ou si un générateur de requêtes dynamiques complexe échoue à injecter la condition, une violation massive des données se produit.
La RLS élimine ce risque en appliquant des contraintes au niveau de l'exécution SQL. Même si un administrateur accorde à un utilisateur des autorisations larges, la politique RLS agit comme un filtre invisible, limitant les résultats uniquement aux lignes que l'utilisateur est autorisé à accéder. Cette stratégie de défense en profondeur est cruciale pour les cadres de conformité tels que le RGPD, la HIPAA et le SOC2.
Mise en œuvre de la RLS dans PostgreSQL
Examinons une mise en œuvre pratique utilisant PostgreSQL, la norme de l'industrie pour les fonctionnalités avancées de RLS. Le concept central consiste à créer une fonction qui détermine le contexte du locataire actuel et à définir des politiques qui appliquent ce contexte.
Étape 1 : Établir le contexte du locataire
D'abord, nous avons besoin d'un moyen d'identifier le locataire actuel. Cela se fait généralement en définissant une variable de session ou en utilisant une revendication d'un jeton d'authentification (comme JWT). Pour cet exemple, nous utiliserons un paramètre de session personnalisé.
-- Activer le contexte de sécurité
-- Dans une vraie application, ceci est défini après l'authentification
SET app.current_tenant_id = 'tenant_001';
-- Fonction utilitaire pour récupérer l'ID du locataire
CREATE OR REPLACE FUNCTION get_current_tenant_id()
RETURNS UUID AS $$
BEGIN
RETURN current_setting('app.current_tenant_id')::UUID;
END;
$$ LANGUAGE plpgsql SECURITY DEFINER;
Étape 2 : Définir les politiques de sécurité
Maintenant, nous appliquons des politiques aux tables spécifiques. Voici un exemple pour une table orders standard.
-- Activer la RLS sur la table
ALTER TABLE orders ENABLE ROW LEVEL SECURITY;
-- Politique : Les utilisateurs ne peuvent voir que leurs propres données
CREATE POLICY tenant_isolation_policy ON orders
USING (tenant_id = get_current_tenant_id());
-- Optionnel : Politique pour les utilisateurs administratifs (superutilisateurs)
-- Si vous avez besoin que les administrateurs voient toutes les données, vous pourriez créer une deuxième politique
CREATE POLICY admin_override_policy ON orders
FOR ALL
TO admin_role
USING (true);
Remarquez la distinction entre la clause USING et la clause WITH CHECK. La clause USING contrôle quelles lignes sont retournées lors d'un SELECT. La clause WITH CHECK contrôle quelles lignes peuvent être insérées ou mises à jour, garantissant que les utilisateurs ne peuvent pas accidentellement (ou malveillamment) modifier des enregistrements appartenant à d'autres locataires.
Étape 3 : Gérer l'identité
Dans de nombreuses applications, vous devez également suivre qui a créé un enregistrement. Si votre table d'utilisateurs inclut un user_id, vous pouvez étendre la politique pour garantir que les utilisateurs n'accèdent qu'aux données qu'ils ont personnellement créées, en plus des données de leur locataire.
-- Politique plus granulaire : Les utilisateurs ne peuvent voir que leurs propres commandes
CREATE POLICY user_isolation_policy ON orders
FOR SELECT
USING (
tenant_id = get_current_tenant_id()
AND user_id = current_user_id()
);
Considérations de performance
Bien que la RLS soit sécurisée, elle peut avoir un impact sur les performances si elle n'est pas correctement indexée. Le moteur de base de données doit évaluer la politique pour chaque ligne accédée. Par conséquent, il est crucial que votre colonne tenant_id soit indexée. Sans index approprié, la base de données pourrait effectuer un balayage séquentiel pour chaque requête, entraînant des pics de latence significatifs à mesure que le volume de données augmente.
De plus, gardez à l'esprit que les politiques RLS ajoutent une surcharge à la phase de planification des requêtes. Effectuez des tests de charge de vos requêtes pour vous assurer que la surcharge de sécurité ne dépasse pas vos budgets de performance. Pour les charges de travail fortement en lecture, envisagez des réplicas en lecture ou des stratégies de mise en cache pour atténuer l'impact de l'évaluation des politiques sur les opérations d'écriture.
Conclusion
La mise en œuvre de la sécurité au niveau des lignes est l'un des moyens les plus efficaces de sécuriser une architecture multi-locataire. Elle déplace la sécurité d'un modèle de logique application "faire confiance mais vérifier" vers un modèle de base de données "ne jamais faire confiance, toujours vérifier". En tirant parti de la RLS, les ingénieurs de bases de données peuvent fournir une fondation de sécurité robuste qui évolue avec l'entreprise, protège les données sensibles et maintient la conformité avec une surcharge minimale au niveau de l'application.
Lorsque vous intégrez la RLS dans votre pile, n'oubliez pas de tester les cas limites, de garantir un indexation appropriée et de documenter clairement vos politiques de sécurité. Dans le monde de la multi-location, la sécurité n'est pas une fonctionnalité ; c'est le fondement.