Blog
Analyses techniques

Widgets de chat embarquables : architecture et sécurité

Le loader en deux étapes et l'iframe sandboxé derrière le widget de Simmetric Chat, avec sessions isolées, rate limiting et accès en couches à la base de connaissances.

Simmetric Team7 min de lecture30/04/2026

Votre base de connaissances est une surface produit

Les documents que votre équipe écrit - documentation produit, articles de support, wikis internes - répondent aux mêmes questions que les visiteurs de votre site se posent déjà. Un widget de chat embarqué transforme cette base de connaissances en porte d'entrée conversationnelle : les visiteurs posent leurs questions avec leurs mots, et les réponses arrivent avec des citations, ancrées dans les workspaces que vous choisissez d'exposer.

La partie difficile n'est pas le chat ; c'est d'embarquer un logiciel quasi non fiable dans la page de quelqu'un d'autre sans devenir une surface d'attaque. Le système de widget de Simmetric Chat - palier Enterprise - est construit autour de cette contrainte. Cet article parcourt l'architecture : l'embed en deux étapes, le runtime sandboxé, l'isolation des sessions, le rate limiting et le modèle d'accès en couches à la base de connaissances qui décide ce que la question d'un visiteur peut atteindre, et ce qu'elle ne peut pas.

L'embed en deux étapes : loader et iframe sandboxé

Le widget se livre en deux étapes, et la séparation est délibérée :

  1. Le script loader. Ajoutez une balise script à votre site, pointée vers le service widget avec votre ID de widget. Le loader est un petit fichier JavaScript qui crée une iframe dans la page hôte.
  2. Le panneau iframe. L'iframe charge une application Preact autonome - compilée en bundle IIFE avec CSS en ligne - qui rend le bouton flottant, le panneau de chat et chaque interaction dans son propre document.
html
<script src="https://your-simmetric-chat.com/widget/loader.js?widgetId=WIDGET_ID"></script>

Pourquoi ne pas injecter l'interface de chat directement dans la page hôte ? Parce que l'interpolation de chaînes de contenu non fiable dans une page hôte est le chemin XSS classique - et le flux de contenu entier d'un widget est quasi non fiable : il contient la sortie du modèle, des extraits de documents et les saisies des visiteurs. Faire tourner l'interface dans une iframe sandboxée à origine opaque supprime entièrement cette classe de risque : le contenu du chat ne s'exécute jamais dans le contexte de votre page, et les styles du widget ne peuvent pas entrer en collision avec les vôtres (ni l'inverse).

Les anciennes routes d'embed par interpolation de chaînes ont été retirées du produit exactement pour cette raison - le pattern en deux étapes n'est pas une option parmi d'autres, c'est l'embed. La compilation IIFE du widget existe pour la même raison : le panneau est un fichier autonome unique, sans système de modules ni chargement d'assets externes à l'exécution, ce qui garde la surface réseau de l'iframe prévisible.

Ce qui tourne où

Le widget touche trois processus, et connaître la carte des frontières explique chaque propriété de sécurité qui suit :

  • Le serveur principal possède le CRUD et le branding des widgets, le rattachement aux workspaces, l'API interne que le service widget appelle, et les endpoints lead et analytics côté admin. Tout le routeur interne des widgets est contraint à la feature - quand la feature widget est éteinte, il répond 402 sur chaque route.
  • Le service widget est la surface d'embed publique : loader JS, HTML de l'iframe, création des sessions, livraison de la configuration en cache, le proxy SSE du chat vers le serveur principal et l'endpoint lead. Il s'authentifie auprès du serveur principal avec une clé API dédiée et porte ses propres rate limiters.
  • L'iframe est le client Preact : bootstrap de la session, streaming SSE, affichage des citations, questions suggérées, barrière de consentement et la carte lead.

Le proxy du chat relaie le même format d'événements que l'API de streaming interne - messages d'état, annonces de plan, tokens en streaming, citations et événements terminaux done/error - donc ce qu'un visiteur vit est le même pipeline que votre équipe utilise en interne, avec les permissions du widget.

Sessions isolées pour visiteurs anonymes

Les visiteurs ne s'authentifient jamais ; le widget forge l'identité à leur place. Quand le panneau s'ouvre, il demande une session au service widget, qui demande au serveur principal d'en créer une : un token hexadécimal de 256 bits, côté serveur, avec une expiration à 24 heures. Chaque requête ultérieure du widget valide contre cette session - aucun JWT des apps de vos utilisateurs, aucun cookie qui traverse les origines, aucune credential environnementale d'aucune sorte.

L'isolation des sessions est ce qui garde l'état d'un visiteur hors de portée d'un autre : les conversations sont anonymes, les compteurs et l'état des rate limits s'accrochent au token de session plutôt qu'à une IP ou à une empreinte, et la page hôte ne voit jamais le token - il vit dans le storage de l'iframe elle-même, échangé avec l'hôte via un petit protocole postMessage versionné que les deux côtés valident contre l'exact contentWindow qu'ils attendent, de sorte que les messages contrefaits par d'autres scripts sont ignorés.

La livraison de la configuration suit la même discipline. La configuration du widget - branding, message de bienvenue, questions suggérées - est livrée à l'iframe via un bloc JSON dans le document de l'iframe elle-même, injecté côté serveur, et non via des paramètres d'URL qu'un visiteur pourrait manipuler. Le visiteur ne peut influencer rien que le serveur ne résolve : un ajout récent le consacre davantage, en permettant à la configuration de chaque widget de sélectionner le provider LLM et le modèle qui sert ses réponses de chat, résolus côté serveur à chaque requête de chat - jamais fournis par le client.

Le rate limiting comme contrat de surface publique

Un widget est un endpoint public sur votre infrastructure, donc le contrôle des abus fait partie de l'architecture, pas des ajouts après coup. Les limites sont stratifiées par surface :

  • Limites de chat par session : 20 messages par heure et 5 conversations par jour par session ; les limites dépassées répondent 429 avec une indication retryAfter, et le widget affiche un avertissement de rate limit plutôt que d'échouer en silence.
  • Limiter API par clé : les appels du service widget au serveur principal sont limités par clé API hachée, pour qu'un déploiement défaillant ne puisse pas épuiser la surface partagée.
  • Limiter lead : l'endpoint de lead capture est la barrière la plus stricte, à 3 requêtes par heure, parce qu'un formulaire public est exactement où se concentre l'abus d'automatisation.
  • CORS fail-closed : l'en-tête Origin de chaque requête est validé contre la liste d'origines autorisées du widget lui-même, modifiable depuis le panneau d'administration. Les requêtes de preflight non autorisées sont rejetées - et si la vérification d'origine ne peut pas consulter la base de données, l'accès est refusé plutôt qu'accordé.

Le résultat est une surface de chat publique qui dégrade gracieusement sous charge ou abus plutôt que de s'effondrer ou d'exclure tout le monde.

Accès en couches à la base de connaissances

Un widget doit répondre à partir de la connaissance que vous avez l'intention de publier - rien de plus. La stratification de l'accès est imposée structurellement plutôt que par prompt :

  • Liste blanche des workspaces. Chaque widget est rattaché à un ou plusieurs workspaces, et la liste blanche est remplacée atomiquement à chaque mise à jour. La connaissance à laquelle un widget peut puiser est exactement l'union des collections vectorielles et des documents des workspaces rattachés - un widget de support peut voir votre workspace de documentation publique sans jamais toucher aux données des projets internes.
  • Contrainte de feature. La création de widgets exige le flag de la feature widget et respecte la limite numérique de widgets du palier, pour que la surface ne puisse pas grandir en silence.
  • Citations comme frontières. Les réponses arrivent avec des citations qui ne pointent que dans les workspaces autorisés - un visiteur peut vérifier d'où vient une réponse, et la trace des citations ne peut pas pointer hors de la liste blanche.

Le branding - couleur primaire, nom du bot, logos, position - est une configuration par widget rendue via des propriétés CSS personnalisées dans l'iframe, pour qu'un cabinet de conseil puisse présenter l'assistant comme étant celui de son client. Les déploiements de palier Community reçoivent des défauts sensés appliqués quand les champs sont vides ; la personnalisation complète et le white-labeling sont des flags enterprise.

La checklist de sécurité, résumée

Chaque couche ci-dessus existe parce qu'un widget est du logiciel que vous expédiez dans la frontière de confiance de quelqu'un d'autre :

  • Loader en deux étapes + iframe sandboxée à origine opaque - aucun XSS par interpolation de chaînes
  • Configuration injectée par le serveur - rien de pertinent pour la sécurité n'est fourni par le client
  • Model pin résolu par le serveur - le modèle qui répond ne peut pas être manipulé par le navigateur du visiteur
  • Sessions anonymes à 256 bits avec expiration à 24 heures - aucune credential environnementale
  • Rate limits en couches avec 429 + retryAfter - dégradation gracieuse sous abus
  • CORS dynamique fail-closed - la confiance d'origine décidée en exactement un point
  • Liste blanche des workspaces atomique - l'accès à la connaissance est structurel, pas espéré dans le prompt

Du panneau d'administration à l'extrait fonctionnel, le trajet prend des minutes : créez le widget, mettez les workspaces en liste blanche, ajoutez les origines, collez la balise script. Le guide complet d'intégration couvre le branding, le protocole simmetric:* postMessage et le lead capture de bout en bout.

Lire le guide complet dans la doc →

Articles associés