Widgets de chat embutíveis: arquitetura e segurança
O loader em duas etapas e o iframe em sandbox por trás do widget do Simmetric Chat, com sessões isoladas, rate limiting e acesso em camadas à base de conhecimento.
Sua base de conhecimento é uma superfície de produto
Os documentos que sua equipe escreve - documentação de produto, artigos de suporte, wikis internos - respondem às mesmas perguntas que os visitantes do seu site já estão fazendo. Um widget de chat embutido transforma essa base de conhecimento em uma porta de entrada conversacional: os visitantes perguntam com suas próprias palavras, e as respostas fluem com citações, ancoradas nos workspaces que você decide expor.
A parte difícil não é o chat; é embutir software quase não confiável na página de outra pessoa sem se tornar uma superfície de ataque. O sistema de widget do Simmetric Chat - nível Enterprise - é construído em torno dessa restrição. Este artigo percorre a arquitetura: o embed em duas etapas, o runtime em sandbox, o isolamento de sessões, o rate limiting e o modelo de acesso em camadas à base de conhecimento que decide o que a pergunta de um visitante pode alcançar, e o que não pode.
O embed em duas etapas: loader e iframe em sandbox
O widget é entregue em duas etapas, e a divisão é deliberada:
- O script loader. Adicione uma tag script ao seu site, apontada para o serviço de widget com o seu ID de widget. O loader é um pequeno arquivo JavaScript que cria um iframe na página hospedeira.
- O painel iframe. O iframe carrega uma aplicação Preact autônoma - compilada como bundle IIFE com CSS inline - que renderiza o botão flutuante, o painel de chat e cada interação dentro do seu próprio documento.
<script src="https://your-simmetric-chat.com/widget/loader.js?widgetId=WIDGET_ID"></script>Por que não injetar a interface de chat diretamente na página hospedeira? Porque interpolar strings de conteúdo não confiável em uma página hospedeira é o caminho clássico do XSS - e todo o fluxo de conteúdo de um widget é quase não confiável: contém saída do modelo, trechos de documentos e entradas dos visitantes. Rodar a interface dentro de um iframe em sandbox de origem opaca elimina essa classe de risco por completo: o conteúdo do chat nunca é executado no contexto da sua página, e os estilos do widget não podem colidir com os seus (nem vice-versa).
As antigas rotas de embed por interpolação de strings foram removidas do produto exatamente por essa razão - o padrão em duas etapas não é uma opção entre várias, é o embed. A compilação IIFE do widget existe pelo mesmo motivo: o painel é um único arquivo autônomo, sem sistema de módulos e sem carregamento de assets externos em tempo de execução, o que mantém previsível a superfície de rede do iframe.
O que roda onde
O widget toca três processos, e conhecer o mapa de fronteiras explica cada propriedade de segurança que segue:
- O servidor principal possui o CRUD e o branding dos widgets, a vinculação aos workspaces, a API interna que o serviço de widget chama, e os endpoints de lead e analytics do lado admin. Todo o router interno de widgets é atado à feature - quando a feature widget está desligada, ele responde
402em cada rota. - O serviço de widget é a superfície de embed pública: loader JS, HTML do iframe, criação de sessões, entrega da configuração em cache, o proxy SSE do chat para o servidor principal e o endpoint de lead. Ele se autentica no servidor principal com uma chave de API dedicada e carrega seus próprios rate limiters.
- O iframe é o cliente Preact: bootstrap da sessão, streaming SSE, exibição de citações, perguntas sugeridas, a barreira de consentimento e o cartão de lead.
O proxy do chat encaminha o mesmo formato de eventos da API de streaming interna - mensagens de status, anúncios de plano, tokens em streaming, citações e eventos terminais done/error - então o que um visitante experimenta é o mesmo pipeline que sua equipe usa internamente, com as permissões do widget.
Sessões isoladas para visitantes anônimos
Os visitantes nunca se autenticam; o widget cunha a identidade no lugar deles. Quando o painel abre, ele pede uma sessão ao serviço de widget, que pede ao servidor principal para criar uma: um token hexadecimal de 256 bits, do lado do servidor, com expiração de 24 horas. Cada requisição subsequente do widget valida contra essa sessão - nenhum JWT dos apps dos seus usuários, nenhum cookie atravessando origens, nenhuma credencial ambiental de qualquer tipo.
O isolamento de sessões é o que mantém o estado de um visitante fora do alcance de outro: as conversas são anônimas, os contadores e o estado dos rate limits se prendem ao token de sessão em vez de a um IP ou a uma impressão digital, e a página hospedeira nunca vê o token - ele vive no storage do próprio iframe, trocado com o host por um pequeno protocolo postMessage versionado que ambos os lados validam contra o exato contentWindow que esperam, de modo que mensagens forjadas por outros scripts são ignoradas.
A entrega da configuração segue a mesma disciplina. A configuração do widget - branding, mensagem de boas-vindas, perguntas sugeridas - é entregue ao iframe por um bloco JSON no documento do próprio iframe, injetado do lado do servidor, não por parâmetros de URL que um visitante poderia manipular. O visitante não pode influenciar nada que o servidor não resolva: uma adição recente consagra isso ainda mais, permitindo que a configuração de cada widget selecione o provider LLM e o modelo que serve suas respostas de chat, resolvidos do lado do servidor a cada requisição de chat - nunca fornecidos pelo cliente.
O rate limiting como contrato de superfície pública
Um widget é um endpoint público na sua infraestrutura, então o controle de abusos faz parte da arquitetura, não é um acréscimo tardio. Os limites são estratificados por superfície:
- Limites de chat por sessão: 20 mensagens por hora e 5 conversas por dia por sessão; os limites excedidos respondem
429com uma dicaretryAfter, e o widget mostra um aviso de rate limit em vez de falhar em silêncio. - Limiter de API por chave: as chamadas do serviço de widget ao servidor principal são limitadas por chave de API hasheada, para que uma implantação defeituosa não possa esgotar a superfície compartilhada.
- Limiter de lead: o endpoint de lead capture é a barreira mais estreita, com 3 requisições por hora, porque um formulário público é exatamente onde o abuso de automação se concentra.
- CORS fail-closed: o cabeçalho
Originde cada requisição é validado contra a lista de origens permitidas do próprio widget, editável pelo painel de administração. Requisições de preflight não permitidas são rejeitadas - e se a verificação de origem não puder consultar o banco de dados, o acesso é negado em vez de concedido.
O resultado é uma superfície de chat pública que degrada graciosamente sob carga ou abuso em vez de colapsar ou excluir todos.
Acesso em camadas à base de conhecimento
Um widget deveria responder a partir do conhecimento que você pretende publicar - nada mais. A estratificação do acesso é imposta estruturalmente em vez de por prompt:
- Whitelist de workspaces. Cada widget é vinculado a um ou mais workspaces, e a whitelist é substituída atomicamente a cada atualização. O conhecimento de que um widget pode extrair é exatamente a união das coleções vetoriais e dos documentos dos workspaces vinculados - um widget de suporte pode ver o seu workspace de documentação pública sem nunca tocar os dados de projetos internos.
- Amarração à feature. A criação de widgets exige o flag da feature widget e respeita o limite numérico de widgets do plano, para que a superfície não possa crescer em silêncio.
- Citações como fronteiras. As respostas fluem com citações que apontam apenas para workspaces permitidos - um visitante pode verificar de onde vem uma resposta, e a trilha de citações não pode apontar para fora da whitelist.
O branding - cor primária, nome do bot, logos, posição - é configuração por widget renderizada via propriedades CSS customizadas dentro do iframe, para que uma consultoria possa apresentar o assistente como sendo do seu cliente. As implantações de nível Community recebem padrões sensatos aplicados quando os campos estão vazios; a personalização completa e o white-labeling são flags enterprise.
A checklist de segurança, resumida
Cada camada acima existe porque um widget é software que você envia para dentro da fronteira de confiança de outra pessoa:
- Loader em duas etapas + iframe em sandbox de origem opaca - sem XSS por interpolação de strings
- Configuração injetada pelo servidor - nada relevante para a segurança é fornecido pelo cliente
- Model pin resolvido pelo servidor - o modelo que responde não pode ser manipulado pelo navegador do visitante
- Sessões anônimas de 256 bits com expiração de 24 horas - sem credenciais ambientais
- Rate limits em camadas com
429+retryAfter- degradação graciosa sob abuso - CORS dinâmico fail-closed - a confiança de origem decidida em exatamente um ponto
- Whitelist de workspaces atômica - o acesso ao conhecimento é estrutural, não esperado no prompt
Do painel de administração ao snippet funcionando o percurso são minutos: crie o widget, coloque os workspaces na whitelist, adicione as origens, cole a tag script. O guia completo de integração cobre branding, o protocolo simmetric:* postMessage e o lead capture de ponta a ponta.


