Seguridad y CSP
Protege los agentes incrustados con listas de orígenes permitidos, políticas del navegador e instalación segura de credenciales.
Protege los agentes incrustados con listas de orígenes permitidos, políticas del navegador e instalación segura de credenciales.
Lista de orígenes permitidos#
La incrustación falla por defecto (fail closed) a menos que esté habilitada y el origen padre exacto del navegador esté permitido. Un origen es scheme://hostname[:port]; las rutas y las cadenas de consulta no forman parte de él.
| Página anfitriona | Valor de la lista de permitidos |
|---|---|
https://example.com/pricing | https://example.com |
https://www.example.com/help | https://www.example.com |
https://preview.example.com:8443/demo | https://preview.example.com:8443 |
http://localhost:3000 | http://localhost:3000 |
Añade por separado los orígenes de producción, de vista previa y locales. No añadas un comodín para resolver un error de instalación: de lo contrario, cualquiera que copie un slug público podría incrustar el agente y consumir los créditos del sitio.
Content Security Policy#
El cargador necesita permiso de script, el widget necesita permiso de frame y la solicitud de aspecto del cargador puede necesitar permiso de conexión. Combina estas fuentes con las directivas existentes del sitio anfitrión; no sustituyas una política consolidada por este ejemplo mínimo.
Content-Security-Policy:
default-src 'self';
script-src 'self' https://makeagent.fast;
frame-src https://makeagent.fast;
connect-src 'self' https://makeagent.fast;Si el sitio usa script-src-elem, añade también https://makeagent.fast ahí. El cargador externo no requiere unsafe-inline, pero el framework del propio sitio anfitrión puede tener requisitos independientes de nonce o hash. Una política en modo solo informe es útil durante las pruebas:
Content-Security-Policy-Report-Only:
script-src 'self' https://makeagent.fast;
frame-src https://makeagent.fast;
connect-src 'self' https://makeagent.fast;Cuando la consola del navegador informe de una infracción de CSP, modifica la directiva indicada (no default-src de forma amplia) y vuelve a probar la página pública.
Micrófono y permisos del navegador#
El cargador crea el iframe con allow="microphone; autoplay; clipboard-write". Una Permissions Policy más estricta a nivel del sitio anfitrión puede anular ese permiso. Si la voz está habilitada, permite el origen del frame de Make Agent Fast en la respuesta del sitio anfitrión:
Permissions-Policy: microphone=(self "https://makeagent.fast")El visitante también debe conceder acceso al micrófono en el navegador. Prueba desde HTTPS; los navegadores normalmente restringen el acceso al micrófono en orígenes públicos no seguros. El chat de texto no requiere permiso de micrófono.
Límite de mensajes y frames#
El cargador solo acepta maf:ready, maf:open, maf:close y los mensajes del ciclo de vida del tema asociados al slug del sitio actual. Los mensajes entrantes del frame se comprueban contra https://makeagent.fast. No copies el cargador en tu propio bundle ni elimines esta validación de origen.
El código de la página anfitriona no debe intentar acceder al DOM del iframe. El frame es de origen cruzado por diseño, y la superficie de integración compatible es el cargador, sus atributos data-* documentados y los métodos de identidad que se describen más abajo.
Secretos e identidad del visitante#
El slug del sitio y la URL del cargador son públicos. Todo lo demás debe quedar fuera del código fuente de la página:
- Nunca añadas al snippet una clave de API de MAF, un token de OAuth, una clave de proveedor de IA, un token de conector ni un secreto de webhook.
- Nunca incluyas un secreto en un bundle de JavaScript ni lo expongas mediante
NEXT_PUBLIC_*,VITE_*u otras variables de entorno del navegador similares. - Usa una integración de API del lado del servidor cuando tu aplicación deba crear sitios, actualizar agentes u obtener datos privados del espacio de trabajo.
- No pongas direcciones de correo, ID de cuenta ni otros datos del visitante en
data-label,data-localeni en parámetros de consulta.
Identificar a los clientes con sesión iniciada#
En Agente → Acciones → Clientes con sesión iniciada, crea un secreto y cópialo cuando se muestre. Guárdalo únicamente en tu servidor como MAF_SECRET_KEY; nunca lo pongas en código del navegador, en variables de entorno públicas ni en un repositorio. Este secreto del agente es independiente de la clave de API del espacio de trabajo. La rotación invalida los tokens antiguos y cambia la clave de firma de Lookups; eliminarlo desactiva la identidad verificada del cliente y las consultas hasta que se configure de nuevo un secreto.
Usa la URL actualizada del cargador https://makeagent.fast/embed.js?v=20261022-open en tu snippet existente. Las URL con versiones anteriores pueden permanecer en caché durante un año y no incluyen este SDK. Mantén la lista de orígenes permitidos y las protecciones de CSP.
Tu servidor autenticado debe emitir el token usando el cliente de su propia sesión. Nunca aceptes un ID de cliente arbitrario del navegador como la identidad que se va a firmar. Este helper se ejecuta únicamente en el servidor:
import { createHmac } from "node:crypto";
export function signCustomerIdentity(customer) {
const secret = process.env.MAF_SECRET_KEY;
if (!secret) throw new Error("MAF_SECRET_KEY is required");
const encode = (value) => Buffer.from(JSON.stringify(value)).toString("base64url");
const now = Math.floor(Date.now() / 1000);
const header = encode({ alg: "HS256", typ: "JWT" });
const payload = encode({
sub: customer.id,
email: customer.email,
name: customer.name,
iat: now,
exp: now + 3600,
});
const body = `${header}.${payload}`;
const signature = createHmac("sha256", secret).update(body).digest("base64url");
return `${body}.${signature}`;
}Exige el encabezado alg: "HS256" y typ: "JWT". sub es una cadena estable no vacía de hasta 128 caracteres; exp es una marca de tiempo Unix entera en segundos, en el futuro y como máximo 24 horas por delante. iat opcional es un entero; email opcional debe ser válido y tener como máximo 160 caracteres; name opcional tiene como máximo 120. El token completo debe caber en 2048 caracteres. Omite los claims opcionales que no necesites. Los payloads de JWT están firmados, no cifrados.
Devuelve únicamente el token a esa sesión autenticada del navegador a través de HTTPS, con Cache-Control: no-store. No lo registres en logs, no lo añadas a las URL ni lo guardes en localStorage. Una vez cargado embed.js, pasa ese token de la respuesta al widget:
window.MakeAgentFast.identify(token);Para la inicialización antes de que se ejecute el cargador, define en su lugar window.MakeAgentFastSettings = { identityToken: token } en el arranque de tu aplicación. El cargador conserva el token más reciente en memoria y lo reenvía cuando el frame está listo. Actualiza el token desde tu servidor autenticado antes de que caduque y vuelve a llamar a identify. Cada solicitud de chat lo incluye; las solicitudes de conversación usan x-maf-identity. Una identidad verificada del sitio anfitrión nunca depende de una cookie af_xid.
Cuando tu aplicación cierre sesión, llama a esto antes de mostrar el chat de otra cuenta y borra cualquier token de arranque que tu aplicación haya conservado:
window.MakeAgentFast.logout();Esto restablece la identidad del widget y la caché local de conversaciones; no cierra la sesión del cliente en tu propia aplicación ni revoca un token copiado. El sub de un cliente distinto también borra la caché del widget. Los tokens no válidos, caducados o de otro agente no pueden acceder al historial de ese cliente con sesión iniciada y recurren a la ruta ordinaria de visitante. Usa caducidades cortas y mantén separado el secreto de cada agente. El ensayo del propietario ignora los tokens de identidad; comprueba el aislamiento en la incrustación real usando dos cuentas de prueba distintas.
Consentimiento, privacidad y retención#
Clasifica la incrustación según tu política de privacidad y consentimiento. Si tu gestor de consentimiento bloquea los scripts de terceros no esenciales, carga embed.js solo después del consentimiento correspondiente. Hasta entonces, muestra un enlace normal o un marcador de posición deshabilitado en lugar de eludir el gestor.
Informa a los visitantes cuando la experiencia use IA, grabación de micrófono, captura de leads, analíticas o un proveedor de mensajería externo, según lo exijan tu política y la legislación aplicable. Confirma el flujo de retención y eliminación de conversaciones de tu sitio antes del lanzamiento; desconectar o eliminar la incrustación no borra por sí solo las conversaciones existentes.
Lista de comprobación de seguridad para el lanzamiento#
- El sitio web del cliente está desplegado y su agente incrustado está habilitado y probado.
- Solo están en la lista de permitidos los orígenes padre exactos y previstos.
- La CSP del sitio anfitrión permite el script, el frame y la solicitud de aspecto sin comodines amplios.
- El micrófono solo se permite si la voz está habilitada y se ha probado a través de HTTPS.
- Ninguna credencial ni valor privado del visitante aparece en el HTML, en la salida del bundle ni en los logs del navegador.
- El comportamiento del gestor de consentimiento se prueba antes y después del consentimiento.
- Un snippet copiado falla en un origen que no está en la lista de permitidos.