Безопасность и CSP
Защитите встроенных агентов разрешёнными списками origin, политиками браузера и установкой без утечки учётных данных.
Защитите встроенных агентов разрешёнными списками origin, политиками браузера и установкой без утечки учётных данных.
Разрешённый список origin#
Встраивание отказывает по умолчанию, пока оно не включено и точный родительский origin браузера не разрешён. Origin — это scheme://hostname[:port]; пути и строки запроса в него не входят.
| Страница сайта | Значение для списка |
|---|---|
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 |
Добавляйте продакшен-, предпросмотр- и локальные origin отдельно. Не добавляйте шаблон-звёздочку ради исправления ошибки установки: иначе любой, кто скопирует публичный slug, сможет встроить агента и расходовать кредиты сайта.
Content Security Policy#
Загрузчику нужно разрешение на скрипт, виджету — на фрейм, а запросу внешнего вида загрузчика может понадобиться разрешение на соединение. Объедините эти источники с существующими директивами сайта; не заменяйте зрелую политику этим минимальным примером.
Content-Security-Policy:
default-src 'self';
script-src 'self' https://makeagent.fast;
frame-src https://makeagent.fast;
connect-src 'self' https://makeagent.fast;Если сайт использует script-src-elem, добавьте https://makeagent.fast и туда. Внешнему загрузчику не нужен unsafe-inline, но собственный фреймворк сайта может иметь отдельные требования к nonce или хешам. Политика report-only полезна во время тестирования:
Content-Security-Policy-Report-Only:
script-src 'self' https://makeagent.fast;
frame-src https://makeagent.fast;
connect-src 'self' https://makeagent.fast;Когда консоль браузера сообщает о нарушении CSP, меняйте названную директиву — а не default-src в ширину — и перепроверяйте публичную страницу.
Микрофон и разрешения браузера#
Загрузчик создаёт iframe с allow="microphone; autoplay; clipboard-write". Более строгая Permissions Policy на уровне сайта всё равно может переопределить это разрешение. Если голос включён, разрешите origin фрейма Make Agent Fast в ответе сайта:
Permissions-Policy: microphone=(self "https://makeagent.fast")Посетитель также должен выдать доступ к микрофону в браузере. Проверяйте по HTTPS; браузеры обычно ограничивают доступ к микрофону на незащищённых публичных origin. Текстовый чат не требует разрешения на микрофон.
Граница сообщений и фрейма#
Загрузчик принимает только сообщения maf:ready, maf:open, maf:close и сообщения жизненного цикла темы, связанные с текущим slug сайта. Входящие сообщения фрейма проверяются против https://makeagent.fast. Не копируйте загрузчик в собственный бандл и не удаляйте эту проверку origin.
Код страницы сайта не должен пытаться залезть в DOM iframe. Фрейм cross-origin по замыслу, и поддерживаемая поверхность интеграции — загрузчик, его документированные атрибуты data-* и методы идентификации ниже.
Секреты и идентичность посетителей#
Slug сайта и URL загрузчика публичны. Всё остальное держите вне исходного кода страницы:
- Никогда не добавляйте ключ MAF API, токен OAuth, ключ AI-провайдера, токен коннектора или секрет вебхука в сниппет.
- Никогда не встраивайте секрет в бандл JavaScript и не раскрывайте его через
NEXT_PUBLIC_*,VITE_*или похожие браузерные переменные окружения. - Используйте серверную интеграцию API, когда приложению нужно создавать сайты, обновлять агентов или получать приватные данные рабочего пространства.
- Не помещайте адреса электронной почты, ID аккаунтов и другие данные посетителей в
data-label,data-localeили параметры запроса.
Идентификация вошедших клиентов#
В разделе Агент → Действия → Вошедшие клиенты создайте секрет и скопируйте его при показе. Храните его только на сервере как MAF_SECRET_KEY; не помещайте в браузерный код, публичные переменные окружения или репозиторий. Секрет агента отличается от ключа API рабочего пространства. Ротация делает старые токены недействительными и меняет ключ подписи запросов поиска; удаление отключает подтверждённую идентификацию и поиск до настройки нового секрета.
Обновите URL загрузчика в существующем сниппете до https://makeagent.fast/embed.js?v=20261022-open. Старые версионные URL могут кешироваться год и не предоставляют этот SDK. Сохраните ограничения origin и CSP.
Ваш аутентифицированный сервер должен выпускать токен для клиента из собственной сессии. Не подписывайте произвольный ID клиента, присланный браузером. Эта функция выполняется только на сервере:
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}`;
}Заголовок должен содержать alg: "HS256" и typ: "JWT". sub — стабильная непустая строка до 128 символов; exp — целочисленное время Unix в секундах, в будущем и не далее 24 часов от текущего момента. Необязательный iat — целое число; email — корректный адрес до 160 символов; name — до 120 символов. Полный токен ограничен 2 048 символами. Не передавайте ненужные поля. Содержимое JWT подписано, но не зашифровано.
Верните только токен в эту аутентифицированную сессию браузера по HTTPS с Cache-Control: no-store. Не записывайте его в логи, URL или localStorage. После загрузки embed.js передайте полученный токен виджету:
window.MakeAgentFast.identify(token);Для инициализации до загрузчика установите window.MakeAgentFastSettings = { identityToken: token } в коде запуска приложения. Загрузчик хранит последний токен в памяти и передаёт его готовому фрейму. До истечения срока получите новый токен с сервера и снова вызовите identify. Каждый запрос чата содержит токен; запросы диалогов используют x-maf-identity. Проверенная идентичность сайта не опирается на cookie af_xid.
При выходе из приложения, до показа чата другого аккаунта, вызовите следующее и удалите сохранённый приложением начальный токен:
window.MakeAgentFast.logout();Это сбрасывает идентичность виджета и локальный кеш диалогов, но не завершает сессию вашего приложения и не отзывает скопированный токен. Смена sub на другого клиента тоже очищает кеш. Поддельные, просроченные и подписанные для другого агента токены не открывают историю этого клиента; используется обычный путь посетителя. Выбирайте короткий срок действия и отдельный секрет для каждого агента. Репетиция владельца игнорирует токены; проверяйте изоляцию в настоящем встраивании с двумя тестовыми аккаунтами.
Согласие, конфиденциальность и хранение#
Отнесите встраивание к категории по вашей политике конфиденциальности и согласия. Если менеджер согласия блокирует необязательные сторонние скрипты, загружайте embed.js только после нужного согласия. До этого показывайте обычную ссылку или отключённый плейсхолдер вместо обхода менеджера.
Сообщайте посетителям, когда сценарий использует ИИ, запись микрофона, сбор заявок, аналитику или внешнего провайдера сообщений — как требует ваша политика и применимый закон. Перед запуском убедитесь в процессе хранения и удаления диалогов на сайте; отключение или удаление встраивания само по себе не удаляет существующие диалоги.
Чек-лист безопасности при запуске#
- Клиентский сайт развёрнут, и его встроенный агент включён и проверен.
- Разрешены только точные, предназначенные родительские origin.
- CSP сайта разрешает скрипт, фрейм и запрос внешнего вида без широких шаблонов.
- Микрофон разрешён только если голос включён и проверен по HTTPS.
- Ни одно учётное или приватное значение посетителя не появляется в HTML, собранном бандле или логах браузера.
- Поведение менеджера согласия проверено до и после согласия.
- Скопированный сниппет не работает на origin вне разрешённого списка.