sécuriser l’accès et le filtrage des données d’un assistant IA dans un ERP/CRM multi-tenant

Contexte : vous intégrez un assistant IA (RAG / agent) à un ERP/CRM multi‑tenant et vous devez garantir que chaque client ne voit que ses données. Cet article technique explique une architecture pragmatique, les patterns à implémenter (auth, propagation d’identité, isolation, RLS), des snippets opérationnels (Node.js + PostgreSQL) et les erreurs courantes à éviter. Public : CTO, lead dev, architecte.

Pourquoi c’est critique

Un assistant IA peut interroger des bases structurées et des vecteurs de documents. Sans isolation forte, vous risquez des fuites de données clients (incident majeur, GDPR, perte de confiance). La sécurité doit être répartie : périphérie (auth/NAT/ingress), application (authZ, validation), base (RLS / policies) et couche recherche (filtres tenant pour vecteurs).

Vue d’ensemble de l’architecture recommandée

  1. Authentification centralisée (OIDC / JWT) + MFA au besoin.
  2. Propager l’identité et le tenant dans chaque requête service→service (JWT ou mTLS).
  3. Enforcer isolation au niveau base via Row‑Level Security (PostgreSQL) ou équivalent.
  4. Filtrer les recherches vectorielles en ajoutant un champ tenant_id/namespace.
  5. Observabilité et tests de politiques (policy tests, intégration continue).

1) Authentification et propagation d’identité (API / microservices)

Mise en place : front/end authentifie l’utilisateur via OIDC, l’API reçoit un JWT signé. L’API service‑side valide le JWT, extrait tenant_id et rôles, puis transmet l’identité aux appels DB et aux appels aux services d’IA.

Bonnes pratiques : validation stricte des JWT (signature, algorithme, exp), rotation des clés et refresh tokens. Voir les recommandations OWASP pour l’authentification et JWT. ([cheatsheetseries.owasp.org](https://cheatsheetseries.owasp.org/cheatsheets/Authentication_Cheat_Sheet.html?utm_source=openai))

// exemple middleware Express (TypeScript) : extraire tenant et attacher au contexte request
import { Request, Response, NextFunction } from 'express';
import jwt from 'jsonwebtoken';

export function authMiddleware(req: Request, res: Response, next: NextFunction) {
  const auth = req.headers.authorization?.split(' ');
  if (!auth || auth[0] !== 'Bearer') return res.status(401).send('unauth');
  try {
    const payload = jwt.verify(auth[1], process.env.JWTPUB || '');
    // payload devrait contenir tenant_id et roles
    (req as any).user = payload;
    next();
  } catch (e) {
    return res.status(401).send('invalid token');
  }
}

2) Propagation à la base : pattern « session variable » + Row‑Level Security (RLS)

Au lieu d'ajouter manuellement WHERE tenant_id = X partout, déléguez l’isolation à PostgreSQL avec RLS. Le pattern courant :

  • l’API, pour chaque connexion/transaction, appelle SET LOCAL my.app_tenant = 'TENANT_ID' (ou utilise la fonction set_config),
  • les policies RLS utilisent current_setting('my.app_tenant', true) pour filtrer les lignes visibles.

Cela réduit le risque d’oubli et centralise la logique d’isolation dans le SGBD. La documentation PostgreSQL sur RLS et CREATE POLICY est la référence. ([postgresql.org](https://www.postgresql.org/docs/17/ddl-rowsecurity.html?utm_source=openai))

-- activer RLS et créer une policy tenant
ALTER TABLE invoices ENABLE ROW LEVEL SECURITY;

CREATE POLICY tenant_isolation ON invoices
  USING ( tenant_id::text = current_setting('my.app_tenant', true) );

Exemple Node.js avec pg (transactionnelle pour garantir que le setting est scopé à la transaction) :

await client.query('BEGIN');
await client.query("SELECT set_config('my.app_tenant', $1, true)", [tenantId]);
// ... vos requêtes SELECT/INSERT/UPDATE ici ...
await client.query('COMMIT');

Points d’attention RLS

  • Les rôles superuser ou BYPASSRLS peuvent contourner les policies : attention aux droits. ([postgresql.org](https://www.postgresql.org/docs/17/ddl-rowsecurity.html?utm_source=openai))
  • RLS peut impacter l’optimiseur ; testez les requêtes lourdes et profilez les pPlans.
  • Écrire des tests d’intégration qui simulent plusieurs tenants est indispensable.

3) Isolation des données non‑structurées (vecteurs / documents)

Pour la recherche vectorielle (index de chunks/documents) : stocker un champ tenant_id et toujours inclure le filtre tenant dans la requête de recherche. Imposez un namespace par client côté ingestion.

// pseudocode : ajouter filter tenant à la requête de recherche vectorielle
const results = await vectorIndex.search({
  queryEmbedding,
  filter: { tenant_id: tenantId } // essentiel
});

Si vous externalisez l’index (SaaS vector DB), vérifiez le modèle de partage : index par tenant ou index global avec filtre. Index par tenant est souvent plus sûr mais plus coûteux.

4) Autorisation fine (roles, scopes, actions)

En plus du tenant filter, implémentez des checks RBAC au niveau application et, quand pertinent, policies DB qui prennent en compte le rôle. Exemple : policy RLS différente pour les admins d’un tenant vs utilisateurs standards.

CREATE POLICY tenant_admin_can_view_all ON invoices
  USING (
    (current_setting('my.app_tenant', true) = tenant_id::text)
    AND ('admin' = current_setting('my.app_role', true))
  );

Pour propager app_role utilisez la même technique set_config côté backend mais testez la cohérence entre services.

5) Observabilité et tests

  • Ajoutez traces et logs structurés : loggez tenant_id + request_id dans chaque appel.
  • Tests automatisés : scénarios multi‑tenant (tenant A ne voit pas tenant B), tests de montée en charge.
  • Alerting : pics d’erreurs 403/500 sur isolation peuvent signaler une régression.

Erreurs fréquentes et comment les éviter

  • Oublier d’appeler set_config pour les tâches asynchrones / workers → worker doit avoir contexte tenant ou exécuter dans transaction avec setting.
  • Confier uniquement l’isolation à l’application sans RLS → risque d’oubli humain lors d’un nouveau endpoint.
  • Donner trop de droits superuser aux comptes d’application → BYPASSRLS. Minimisez les privilèges. ([postgresql.org](https://www.postgresql.org/docs/17/ddl-rowsecurity.html?utm_source=openai))
  • Ne pas filtrer les vecteurs → fuites via l’assistant même si la base relationnelle est protégée.

Exemples concrets de checklist avant production

  1. Auth : validation JWT + vérification signature (OWASP). ([cheatsheetseries.owasp.org](https://cheatsheetseries.owasp.org/cheatsheets/JSON_Web_Token_Cheat_Sheet.html?utm_source=openai))
  2. DB : RLS activé sur toutes les tables sensibles ; politiques couvertes par tests.
  3. Vector DB : champs tenant, test cross‑tenant.
  4. CI/CD : tests d’intégration multi‑tenant automatisés.
  5. Monitoring : taux d’erreur 403, latence p95, nombre de recherches vectorielles par tenant.

Ressources et lecture

Conclusion — ce que vous saurez faire après lecture

Après ce guide vous aurez une feuille de route pour :

  • implémenter une authentification robuste et propager tenant_id de façon sûre,
  • déléguer l’isolation à PostgreSQL via RLS et comprendre les limites/perf,
  • filtrer correctement les index vectoriels pour éviter les fuites de données,
  • tester et monitorer l’isolation multi‑tenant en production.

Pour approfondir la mise en œuvre (audit de votre architecture, audit RLS, intégration assistant IA), découvrez nos services dédiés à l’ERP/CRM et à l’intelligence artificielle. Services ERP/CRM, Services IA. Vous pouvez aussi consulter des exemples techniques sur PostgreSQL et Node.js.

Besoin d’un audit rapide de sécurité multi‑tenant ? Contactez‑nous.