Skip to content

Безопасность Module Federation ​

Страница предполагает понятный bootstrap, созданный host runtime и знание participant scope. Общие ошибочные решения собраны на странице Анти-паттерны.

Модель доверия ​

MF remote считается отдельным и потенциально недоверенным participant. Даже если его разрабатывает ваша команда, отдельный deployment может содержать несовместимую или скомпрометированную версию.

Host обязан контролировать:

  • URL и версию remote entry;
  • runtime compatibility;
  • tenant/user identity;
  • разрешённые query и mutation namespaces;
  • API-контракт;
  • mount/unmount lifecycle.

Identity задаёт host ​

ts
const ordersScope = runtime.createParticipantScope({
  participantId: "orders-remote",
  participantType: "remote",
  tenantId: authenticatedSession.tenantId,
  userId: authenticatedSession.userId,
  capabilities: {
    queryNamespaces: [["orders"]],
    mutationNamespaces: ["orders"],
  },
})

Не берите tenantId, userId, capabilities или remote URL из query string, localStorage remote либо данных, переданных самим remote.

Least privilege namespaces ​

Разрешайте минимальные префиксы:

ts
capabilities: {
  queryNamespaces: [
    ["orders", "list"],
    ["orders", "details"],
  ],
  mutationNamespaces: [
    "orders.cancel",
    "orders.repeat",
  ],
  globalInvalidation: false,
}

Пустой список означает запрет. globalInvalidation нужен только доверенному composition layer; обычному remote он не требуется.

Transport не входит в scope ​

Scope создаёт stores и ограничивает cache operations. Он не содержит Axios instance, token provider или произвольный execute(url).

Remote получает отдельный typed API contract:

ts
export interface OrdersApi {
  list(config?: AxiosRequestConfig): Promise<AxiosResponse<Order[]>>
  cancel(id: string, config?: AxiosRequestConfig): Promise<AxiosResponse<Order>>
}

Host может реализовать adapter поверх authApi. Remote вызывает adapter только из своего business store.

Private cache по умолчанию ​

Каждый mount получает уникальный instanceId. Private query одного mount не доступна другому mount, даже если совпадают participantId и logical key.

Shared namespace включайте только для данных, безопасных для всех разрешённых participants одной tenant/user partition:

ts
sharedQueryNamespaces: [["shared", "currency"]]

Не делайте shared cache для permissions, персональных черновиков, токенов и платёжных данных.

Remote entry ​

  • загружайте remote только с allowlist origins;
  • используйте HTTPS;
  • задайте CSP для script origins;
  • публикуйте chunks с content hash;
  • логируйте фактическую версию remote;
  • отклоняйте неизвестный protocol до mount;
  • не передавайте secrets через bootstrap options.

Unmount и logout ​

При unmount:

ts
unmountRemote()
ordersScope.dispose()

При logout host сначала запрещает новые private operations, затем освобождает remote scopes, plugins и root runtime. После смены пользователя создаётся новый runtime. Cache старой user partition нельзя переиспользовать.

Что проверять в CI ​

  • несовместимая версия отклоняется до mount;
  • запрещённый namespace не запускает HTTP;
  • два mount не читают private cache друг друга;
  • shared query доступна только разрешённым participants;
  • unmount обнуляет owned stores и requests;
  • remote не импортирует private transport host;
  • bootstrap contract не содержит credentials.