Cross-Origin Storage API: как браузеры планируют дедуплицировать весы ИИ-моделей и библиотек
В истории веб-разработки был период, когда загрузка популярных библиотек вроде React или jQuery с публичных CDN считалась хорошей инженерной практикой. Идея была простой: если тысячи веб-сайтов подключают один и тот же файл react.production.min.js по одинаковому URL, посетитель вашего сайта с высокой вероятностью уже сохранил его в браузерном кэше при посещении другого ресурса. Повторный запрос выполнялся мгновенно из локальной памяти устройства. Один файл скачивался единовременно и использовался всем интернетом.
Однако браузеры были вынуждены сознательно отказаться от этой схемы. Главная причина — угроза приватности. Злоумышленники научились использовать общий HTTP-кэш как вектор межсайтовой слежки (cross-site tracking). Замеряя точное время загрузки ресурса с CDN, сторонний скрипт мог определить, посещал ли пользователь конкретные веб-сайты ранее.
Чтобы закрыть эту дыру в безопасности, вендоры браузеров начали внедрять изоляцию кэша (Cache Partitioning) по домену верхнего уровня (top-level site). Apple Safari реализовала изоляцию первой, Google Chrome добавила разделение в версии 86 (октябрь 2020 года), а Mozilla Firefox — в версии 85. С этого момента один и тот же файл по абсолютно одинаковому URL скачивается и хранится отдельно для каждого независимого сайта. В результате концепция публичных CDN утратила свой ключевой перфоманс-эффект.
Спецификация Cross-Origin Storage (COS) API, разрабатываемая в рамках сообщества WICG (Web Incubator Community Group), предлагает возвращение общего кэша между сайтами, но на принципиально новых условиях безопасности.
Почему существующие браузерные хранилища не справляются
Современный браузер предоставляет разработчикам несколько механизмов локального хранения данных, однако ни один из них не подходит для дедупликации ресурсов между разными сайтами:
| Хранилище | Ключ адресации | Доступ между разными сайтами (Origins)? |
|---|---|---|
| Cache API | URL запроса | Нет (изолирован по домену) |
| IndexedDB | Пользовательский ключ / ID | Нет (изолирован по домену) |
| Origin Private File System (OPFS) | Внутренний путь к файлу | Нет (изолирован по домену) |
| Cross-Origin Storage API | Хеш содержимого (SHA-256) | Да (в рамках разрешенной области) |
Главная проблема традиционных хранилищ заключается в том, что все они адресуются либо по URL, либо по локальным строковым ключам, и намертво изолированы внутри одного origin. Если сайт A и сайт B используют абсолютно идентичные файлы бинарных весов ИИ-модели объемом 8 ГБ, браузер вынужден скачать 16 ГБ данных и занять 16 ГБ на диске пользователя.
С появлением веб-ИИ (Web AI) проблема переросла из локального неудобства в катастрофический расход трафика и диска. Модель Gemma 2 занимает 1.35 ГБ, а Llama-3.1-70B — 33 ГБ. Повторная загрузка подобных объемов для каждого веб-приложения делает исполнение нейросетей в браузере крайне неэффективным.
Архитектура Cross-Origin Storage API и адресация по хешу
Авторы предложения — Томас Штейнер (Thomas Steiner) и Франсуа Бофор (François Beaufort) из Google, а также Кристиан Либель (Christian Liebel) из Thinktecture — предложили изменить сам принцип ключа кэширования. Вместо URL ресурсом управляет криптографический хеш его содержимого (content-addressed storage).
Вся работа API сосредоточена в единственном методе интерфейса navigator.crossOriginStorage:
requestFileHandle(hash, options)
Метод возвращает Promise<FileSystemFileHandle> — стандартный дескриптор из File System API. Это означает, что разработчикам не нужно осваивать новые методы чтения и записи: после получения дескриптора работа с файлом происходит через привычные интерфейсы getFile() и createWritable().
Пример чтения файла из общего кэша
Если ресурс уже был сохранен другим сайтом, его получение выглядит так:
const hash = {
algorithm: "SHA-256",
value: "8f434346648f6b96df89dda901c5176b10a6d83961dd3c1ac88b59b2dc327aa4",
};
try {
const handle = await navigator.crossOriginStorage.requestFileHandle(hash);
const file = await handle.getFile();
console.log("Файл успешно получен из общего кэша:", file.size, "байт");
} catch (err) {
console.log("Файл отсутствует или доступ ограничен:", err.name);
}
Пример сохранения нового файла
Если файл отсутствует в локальном хранилище, приложение скачивает его из сети и сохраняет в Cross-Origin Storage, передавая флаг create: true и список разрешенных доменов:
const handle = await navigator.crossOriginStorage.requestFileHandle(hash, {
create: true,
origins: ["*"], // Разрешить доступ всем сайтам в сети
});
const writable = await handle.createWritable();
await writable.write(fileBlob);
await writable.close();
При записи браузер автоматически перепроверяет полученные байты: он вычисляет SHA-256 контрольную сумму содержимого и сравнивает ее с переданным значением value. Если байты не совпадают, браузер выбрасывает ошибку DataError и отклоняет запись. Благодаря этой проверке никакой сайт не может подменить содержимое популярного файла или внедрить вредоносный код под видом публичной библиотеки.
Параметр origins задает уровень доступа. Если опустить его, файл будет доступен только текущему сайту. Передача списка доменов открывает доступ конкретным партнерам, а значение origins: ["*"] публикует ресурс для всего веба.
Защита от утечек и утечка через зондирование кэша
Адресация по хешу решает проблему подмены данных, но оставляет открытым другой вопрос: не станет ли метод requestFileHandle() новым супер-куки (supercookie)? Если сайт может узнать, находится ли файл с конкретным хешем в кэше пользователя, он может проверить наличие редкого файла, характерного для узкого круга ресурсов, и точно определить историю посещений.
Спецификация Cross-Origin Storage предотвращает слежку с помощью трехзащитного контура:
- Запрет перечисления (No Enumeration): API не содержит методов для листинга сохраненных файлов. Проверить наличие файла можно только в том случае, если приложение уже знает его точный 64-символьный хеш.
- Публичный список хешей (Public Hash List): файл с областью доступа
origins: ["*"]становится доступен другим сайтам только тогда, когда его хеш попадает в специальный реестр популярности (k-anonymity барьер примерно в 100 независимых сайтов). Если файл используется массово (например, оригинальный сборник React или весы Gemma 2), его наличие в кэше не раскрывает индивидуальность пользователя. Если же файл редкий, браузер вернет отказ в доступе. - Искусственный шум (GREASEing): для предотвращения тайминг-атак браузер может случайным образом возвращать ошибку
NotFoundErrorдаже для реально существующего файла. Для гигабайтных весов ИИ-моделей сделано исключение: браузер не будет искусственно заставлять пользователя повторно скачивать десятки гигабайт из-за шума.
Важное отличие текущей версии спецификации от ранних концептов — отсутствие всплывающих диалоговых окон с подтверждением прав для пользователя. Дескриптор, возвращаемый вызовом requestFileHandle(), уже полностью авторизован. Пользовательский контроль осуществляется через стандартные настройки браузера по очистке хранилища и удалению кэшированных файлов.
Декларативный синтаксис и текущий статус
Разработчики спецификации предлагают не только JavaScript API, но и декларативный путь интеграции в HTML через атрибут crossoriginstorage. Он дополняет механизм Subresource Integrity (SRI):
<script
src="https://cdn.example.com/react.production.min.js"
integrity="sha256-8f434346648f6b96df89dda901c5176b10a6d83961dd3c1ac88b59b2dc327aa4"
crossoriginstorage="*"
></script>
В этом случае браузер сам выполняет поиск по хешу из integrity перед отправкой сетевого запроса.
На данный момент Cross-Origin Storage API находится на стадии обсуждения в WICG. Ни один коммерческий браузер пока не поддерживает API нативно, однако сообщество веб-ИИ уже проводит эксперименты. В библиотеке Transformers.js появился экспериментальный флаг experimental_useCrossOriginStorage, а для Chrome разработан расширение-полифил. Стандартизация этого API станет важнейшим шагом на пути к созданию быстрой и приватной платформы для веб-приложений нового поколения.

