Перейти к основному содержимому

2.04. Хранение данных веб-приложений

Всем

Хранение данных веб-приложений

Хранение данных — одна из фундаментальных функций любого веб-приложения. От того, как и где данные хранятся, зависит архитектура приложения, его производительность, безопасность, масштабируемость и поведение в условиях нестабильного сетевого соединения. В современном вебе данные могут находиться на разных уровнях: на клиенте, на сервере, в промежуточных кэшах или распределённых системах. При этом важно различать не только физическое расположение данных, но и их логический контекст: временные (volatile), постоянные (persistent), общие (shared), приватные (private), синхронизируемые (syncable), изолированные (sandboxed).

Веб-приложение не является монолитом, и его данные динамически перемещаются между средами выполнения. Так, загруженный пользователем файл сначала поступает в память браузера, затем может быть сохранён локально — например, в файловой системе через File System Access API или в IndexedDB, — после чего отправляется на сервер, где попадает в базу данных или файловое хранилище. Каждый этап сопровождается выбором механизма хранения, обусловленного требованиями к объёму, скорости доступа, долговечности и безопасности.

Ниже рассматриваются основные уровни и механизмы хранения данных в контексте веб-приложений, с акцентом на клиентскую сторону, а также анализируются сопутствующие концепции: сессии, куки, кэш, приватность и защита от трекинга.


Уровни хранения данных

Все данные, с которыми работает веб-приложение, можно условно разделить на три категории по месту размещения:

  1. Данные, поступающие от пользователя напрямую — файлы, введённый текст, выбранная конфигурация. Такие данные изначально находятся вне приложения и могут быть импортированы, но не сохраняются автоматически. Примеры: изображения, PDF-документы, CSV-файлы, текстовые заметки. Они обрабатываются с помощью File API, FileReader API и, при необходимости, передаются дальше — либо на сервер, либо в локальное хранилище.

  2. Данные, полученные из внешних источников через сеть — результаты вызова REST-, GraphQL- или gRPC-эндпоинтов, ответы от CDN, сторонние фиды. Такие данные приходят в виде структурированных ответов (чаще всего JSON) и обрабатываются в памяти JavaScript. Однако для повышения отзывчивости или обеспечения офлайн-доступа их часто кэшируют — в памяти, в localStorage или в Cache Storage.

  3. Данные, сгенерированные или преобразованные самим приложением — внутреннее состояние (UI-состояние, история навигации), результаты вычислений, метаданные, токены аутентификации. Эти данные требуют явного сохранения, и для этого веб-платформа предоставляет несколько встроенных API.


Локальное хранение на стороне клиента

LocalStorage и SessionStorage

localStorage и sessionStorage — это интерфейсы веб-платформы, предоставляющие простейшее ключ-значное хранилище (key-value store) с синхронным API. Оба интерфейса доступны через объект window и имеют одинаковый методический набор: setItem, getItem, removeItem, clear, key.

Принципиальное различие между ними — в продолжительности жизни данных:

  • localStorage сохраняет данные бессрочно — до тех пор, пока пользователь вручную не очистит хранилище (например, через «Очистить историю» в браузере) или пока приложение не удалит их программно. Данные localStorage привязаны к источнику (origin), то есть к комбинации протокола, домена и порта. Одно и то же приложение, запущенное на http://example.com и https://example.com, будет иметь два независимых localStorage.

  • sessionStorage действует в пределах одного сеанса вкладки. Если пользователь открывает новую вкладку или копирует ссылку во вкладку, создаётся новое изолированное sessionStorage. Но если он обновляет страницу — данные сохраняются. При закрытии вкладки содержимое sessionStorage автоматически удаляется.

Емкость localStorage и sessionStorage варьируется в зависимости от браузера, но обычно составляет от 5 до 10 мегабайт на источник. Важно понимать, что оба хранилища реализованы синхронно: операции блокируют основной поток выполнения, что может вызвать заметные задержки при частом доступе к большому объёму данных. Кроме того, поскольку данные хранятся в виде строк в кодировке UTF-16, сохранение бинарных данных требует предварительного преобразования (например, Base64), что увеличивает объём и снижает эффективность.

Тем не менее, localStorage остаётся популярным решением для хранения небольших структур: настроек интерфейса, токенов (хотя это небезопасно — см. ниже), идентификаторов устройств, флагов первого запуска.

IndexedDB

Когда данные достигают размеров в десятки мегабайт, требуют структурированного поиска, поддержки транзакций или долгосрочного хранения без синхронных блокировок — localStorage становится непригодным. Для таких случаев существует IndexedDB — полноценная клиентская база данных, реализованная в виде не реляционного, но транзакционного хранилища объектов.

IndexedDB работает в асинхронном режиме, использует API на основе событий и промисов (в современных реализациях — IDBRequest, IDBTransaction, IDBObjectStore). Данные хранятся как JavaScript-объекты (или структурированные клонируемые объекты), поддерживаются составные индексы, диапазонные запросы, уникальные и неуникальные ключи. Объём хранилища зависит от доступного места на устройстве и политик браузера: в Chrome, например, квота может достигать 60% от дискового пространства, но не менее 10 ГБ.

Основные сценарии использования IndexedDB:

  • Офлайн-режим: сохранение справочников, пользовательских данных, черновиков форм.
  • Кэширование медиа: аудио, видео, изображения — особенно в PWA, где требуется предзагрузка контента.
  • Сложные клиентские приложения: редакторы кода с историей изменений, локальные базы знаний, игровые сохранения.

Хотя IndexedDB мощен, его API считается низкоуровневым и многословным. Поэтому на практике часто используют библиотеки-обёртки (например, Dexie.js), обеспечивающие удобный декларативный интерфейс и типизацию.

Следует отметить, что IndexedDB изолирован по источнику — как и localStorage. Доступ к базе данных с другого домена невозможен в силу политики одного источника (Same-Origin Policy).

Cache Storage

Отдельный класс хранилищ — Cache Storage, часть Service Worker API. В отличие от localStorage и IndexedDB, Cache Storage предназначен специально для сохранения сетевых ответов: HTTP-запросов и ответов на них. Каждый кэш — именованная коллекция пар «запрос → ответ», где запросы и ответы представлены как объекты Request и Response (из Fetch API).

Ключевая особенность Cache Storage — его интеграция с жизненным циклом Service Worker. При установке Service Worker может предварительно заполнить кэш статическими ресурсами (HTML, CSS, JS), а в процессе работы перехватывать сетевые запросы и отдавать кэшированные ответы при отсутствии интернета. Это лежит в основе Progressive Web Apps, обеспечивающих мгновенную загрузку и работу без сети.

Cache Storage не предназначен для хранения произвольных данных — только для HTTP-контента. Он не поддерживает транзакций в классическом понимании, но допускает атомарное добавление/удаление записей. Ёмкость, как и у IndexedDB, ограничена квотой браузера.


Сеанс, сессия и куки

Веб изначально построен на протоколе HTTP — протоколе без сохранения состояния (stateless). Каждый запрос от клиента к серверу рассматривается как независимый, без учёта предыдущих обращений. Это упрощает масштабирование и кэширование, но создаёт проблему: как серверу понять, что два запроса исходят от одного и того же пользователя, особенно если тот прошёл аутентификацию?

Для решения этой задачи используются механизмы управления состоянием: сеанс (session), сессия (session data) и куки (cookies). Несмотря на схожесть терминов, они относятся к разным уровням реализации.

Что такое сеанс

Сеанс — это логическая последовательность взаимодействий пользователя с веб-приложением в рамках одного непрерывного периода активности. Сеанс начинается с первого запроса (часто — открытия главной страницы) и завершается по истечении заданного периода бездействия (например, 30 минут), либо при явном завершении (выход из аккаунта), либо при закрытии браузера (в зависимости от политики приложения).

Сеанс — это абстракция, а не физическое хранилище. Он может быть привязан к IP-адресу, к пользовательскому устройству, к токену, к браузерной вкладке — но сам по себе не содержит данных. Вместо этого, сеанс идентифицируется уникальным идентификатором, который передаётся между клиентом и сервером.

Что такое сессия

Сессия — это набор данных, связанных с конкретным сеансом и хранящихся на сервере. Это может включать:

  • Идентификатор авторизованного пользователя;
  • Время последнего обращения;
  • Параметры локализации (язык, часовой пояс);
  • Состояние корзины покупок (до оформления заказа);
  • Флаги, например «показано приветственное окно».

Сессионные данные обычно размещаются в сессионном хранилище — это может быть оперативная память (например, встроенный MemoryStore в Express.js), реляционная БД (PostgreSQL), ключ-значное хранилище (Redis, Memcached) или распределённая кэш-система. Redis наиболее распространён, поскольку обеспечивает высокую скорость чтения/записи, поддержку TTL (времени жизни записи) и масштабируемость.

Ключевая особенность: данные сессии никогда не передаются клиенту в открытом виде. Клиент получает только идентификатор сессии — короткую, криптографически стойкую строку (например, s%3Aabc123def456.ZxYwVuTsRqP...), которая служит ключом для поиска соответствующей записи на сервере.

Что такое куки

Куки (HTTP cookies) — это механизм хранения небольших фрагментов данных на стороне клиента, в браузере, с автоматической передачей этих данных при каждом последующем HTTP-запросе к тому же источнику.

Куки устанавливаются сервером через заголовок Set-Cookie в HTTP-ответе. Браузер сохраняет пару имя=значение вместе с метаданными: доменом, путём, временем жизни (Expires или Max-Age), флагами Secure, HttpOnly, SameSite.

Пример заголовка:

Set-Cookie: sid=abc123def456; Path=/; Domain=example.com; Secure; HttpOnly; SameSite=Lax

После этого при каждом запросе к example.com браузер автоматически включает в заголовок Cookie: sid=abc123def456, и сервер извлекает идентификатор для поиска сессии.

Ключевые атрибуты кук:

  • Secure — кука передаётся только по HTTPS. Обязателен для production-сред.
  • HttpOnly — кука недоступна из JavaScript (через document.cookie). Защищает от XSS-атак при компрометации клиентского кода.
  • SameSite — регулирует, при каких условиях кука отправляется в кросс-сайтовых запросах. Возможные значения:
    • Strict — кука отправляется только при прямом переходе (ввод URL, закладка, внутренняя навигация).
    • Lax (по умолчанию в современных браузерах) — разрешает отправку при GET-запросах извне (например, переход по ссылке), но блокирует при POST, iframe, AJAX.
    • None — разрешает отправку в любых кросс-сайтовых контекстах, но требует флага Secure.

Куки — один из самых старых и надёжных механизмов, но они не без недостатков. Размер одной куки ограничен 4 КБ, общее число на домен — около 50, общий объём — до 8 КБ. Кроме того, куки участвуют в каждом HTTP-запросе, что увеличивает объём трафика, особенно при множестве мелких ресурсов.

Cookies vs Sessions

Часто возникает путаница между хранением сессии в куках и хранением идентификатора сессии в куках.

  • Хранение сессии в куках (client-side sessions) — сервер сериализует все сессионные данные (например, в JSON), шифрует или подписывает их (чтобы клиент не мог подделать), и отправляет целиком в куке. Клиент возвращает эту структуру при каждом запросе. Преимущество — отсутствие необходимости в серверном хранилище. Недостатки — ограниченный размер, риск раскрытия данных при неправильной подписи, сложность инвалидации (например, при выходе из аккаунта все ещё действительные куки останутся у браузеров).

  • Хранение идентификатора сессии в куках (server-side sessions) — сервер генерирует одноразовый идентификатор, сохраняет данные в памяти или БД, а клиенту отправляет только этот идентификатор. Это предпочтительный подход в большинстве enterprise-приложений, так как обеспечивает полный контроль над данными, их инвалидацией и безопасностью.


Кэш

Кэширование — это стратегия временного хранения копий данных для уменьшения задержек, снижения нагрузки на сервер и обеспечения работы при отсутствии сети.

В вебе существует несколько уровней кэширования, иерархически вложенных:

  1. Кэш браузера (HTTP-кэш) — автоматический механизм, управляемый HTTP-заголовками (Cache-Control, Expires, ETag, Last-Modified). Браузер сохраняет ответы на диск или в память и при повторных запросах использует их, если политика позволяет. Например, Cache-Control: max-age=3600 означает, что ресурс считается актуальным в течение часа.

  2. Кэш Service Worker (Cache Storage) — программно управляемый кэш на уровне приложения. Позволяет реализовать сложные стратегии: cache-first, network-first, stale-while-revalidate. Именно этот уровень даёт контроль над офлайн-поведением.

  3. Кэш CDN (Content Delivery Network) — промежуточный кэш между сервером и пользователем, размещённый географически ближе к клиенту. Ускоряет доставку статики и снижает нагрузку на origin-сервер.

  4. Кэш на стороне сервера — Redis, Memcached, встроенные кэши ORM и фреймворков. Хранит результаты тяжёлых вычислений, SQL-запросов, рендеринга шаблонов.

Важно понимать, что кэш — это нестабильное хранилище. Данные в нём могут быть удалены в любой момент: при нехватке места, при истечении TTL, при ручной очистке. Поэтому при проектировании следует руководствоваться принципом: кэш ускоряет, но не заменяет источник истины.


Современные и экспериментальные API хранения

Помимо устоявшихся механизмов (localStorage, IndexedDB, Cache Storage), веб-платформа развивается в сторону более гибкого, безопасного и управляемого хранения. Появляются новые интерфейсы, отвечающие на вызовы времени: рост объёмов клиентских данных, требования к приватности, необходимость изоляции, поддержка мульти-профильных и мульти-клиентских сценариев.

Storage Buckets

Storage Buckets — концепция, введённая в рамках инициативы Storage Foundation и реализованная, в частности, в Chrome начиная с версии 96. Она позволяет логически разделять данные одного и того же источника (origin) на независимые «ведра» (buckets), каждое из которых имеет собственные:

  • политики выживания (persistence mode): default, persistent, volatile;
  • квоты (quota limits);
  • уровень изоляции (например, очистка при сбросе настроек, но не при очистке истории);
  • время жизни (expiration).

Например, приложение может создать отдельный bucket для кэшированных медиафайлов, задать ему режим volatile (данные могут быть удалены при нехватке места без предупреждения) и ограничить объём 2 ГБ. При этом данные пользовательских настроек будут храниться в другом bucket’е с режимом persistent — их удаление потребует явного согласия пользователя или сброса данных приложения.

Storage Buckets особенно полезны для:

  • PWA с большим объёмом офлайн-контента;
  • приложений, где часть данных временная (логи, черновики), а часть — критична (настройки, сохранения);
  • сценариев, требующих «мягкой» очистки: например, сброс кэша без потери авторизации.

Доступ к buckets осуществляется через navigator.storageBuckets.open(name, options), возвращающий экземпляр StorageBucket, который, в свою очередь, предоставляет доступ к caches, indexedDB, localStorage внутри этого bucket’а. Таким образом, bucket — это не самостоятельное хранилище, а контекст изоляции для существующих API.

На момент 2025 года поддержка ограничена: полная реализация — в Chromium-браузерах; Firefox и Safari находятся на стадии рассмотрения спецификации. Однако архитектурно это направление считается перспективным и может стать стандартом де-факто.

Storage Foundation API

Storage Foundation API — экспериментальный интерфейс, предоставляющий прямой, байт-ориентированный доступ к дисковому хранилищу в рамках origin. В отличие от IndexedDB, он не оперирует объектами и индексами, а работает с фиксированными буферами (FixedLengthStream) и произвольным доступом (FileHandle), что позволяет реализовывать собственные форматы хранения: B+ деревья, WAL-журналы, кастомные СУБД.

Ключевая особенность — возможность делиться хранилищем между разными источниками при явном согласии пользователя через механизм storage sharing. Например, приложение app.example.com и его расширение chrome-extension://abc123 могут получить доступ к одному и тому же хранилищу, если пользователь подтвердит запрос.

Это важно для экосистем: офисные пакеты, облака с десктопными клиентами, интеграции «веб + расширение». Однако из-за рисков безопасности API требует строгой политики: только HTTPS, только user gesture для инициации обмена, отсутствие доступа к cookies/sessions.

На практике Storage Foundation пока редко используется вне исследовательских и enterprise-проектов. Его сложность и неготовность кросс-браузерной поддержки ограничивают применение, но он служит основой для будущих высокоэффективных клиентских СУБД.

File System Access API

Хотя локальные файлы не являются внутренними данными приложения, их импорт и экспорт — критичная часть многих веб-приложений (редакторы, IDE, медиаплееры). Ранее доступ к файлам был ограничен: input[type=file] позволял только чтение, а сохранение требовало генерации Blob и download-ссылки.

File System Access API (часть File System Living Standard) устраняет эти ограничения:

  • showOpenFilePicker() — открывает системный диалог выбора файлов с возможностью последующего сохранения изменений в тот же файл (при получении прав через requestPermission);
  • showSaveFilePicker() — позволяет сохранять данные напрямую в указанный пользователем путь;
  • showDirectoryPicker() — даёт доступ ко всей директории (например, для проектных редакторов);
  • Полученный FileSystemFileHandle или FileSystemDirectoryHandle можно использовать повторно (с сохранением в IndexedDB), что обеспечивает «постоянное» подключение к файлу между сеансами.

Важно: все операции требуют явного взаимодействия с пользователем (user activation), и браузер отображает индикатор доступа, как при использовании камеры или микрофона. Данные не копируются — приложение работает с реальными файлами на диске, но через sandbox’ированную абстракцию.

API поддерживается в Chrome, Edge, Opera; Firefox и Safari — частично или в экспериментальном режиме.

Extension Storage

Хранилище расширений (chrome.storage / browser.storage) — специализированный механизм для браузерных дополнений. Отличается от localStorage и IndexedDB следующим:

  • Данные привязаны к расширению, а не к origin веб-страницы;
  • Поддерживает синхронизацию между устройствами через аккаунт браузера (storage.sync);
  • Имеет отдельную квоту (обычно ~100 КБ для sync, ~5 МБ для local);
  • API асинхронный и оптимизирован под частые мелкие операции;
  • Доступен из background-скриптов, popup’ов, content-скриптов (с ограничениями).

Например, блокировщик рекламы может сохранять в storage.local список обновлённых фильтров, а в storage.sync — пользовательские правила, чтобы они были доступны на всех устройствах.


Приватность и защита от отслеживания

В условиях роста монетизации пользовательского внимания, сбора поведенческих данных и развития систем профилирования, вопросы приватности перестали быть нишевыми. Современные браузеры — не просто средство отображения контента, а активные агенты защиты пользователя. Они реализуют многоуровневую защиту, сочетающую технические ограничения, политики и интерфейсы для осознанного выбора.

Приватность ≠ анонимность

Приватность в вебе — это контроль над данными. Пользователь должен понимать, что собирается, кем, зачем и как долго. Анонимность — более сильное свойство: невозможность связать действие с конкретной личностью. В стандартном веб-сценарии анонимность невозможна: IP-адрес, User-Agent, разрешение экрана, установленные шрифты, поведенческие паттерны — всё это создаёт уникальный отпечаток устройства (device fingerprint).

Браузеры не скрывают IP (это задача VPN/Tor), но активно борются с усилением отпечатка и его использованием в трекинге.

Отслеживание и его инструменты

Трекинг — это процесс сбора, агрегации и анализа данных о поведении пользователя с целью профилирования. Основные каналы:

  • Куки третьих лиц (third-party cookies) — куки, устанавливаемые доменами, отличными от текущего (например, ads.doubleclick.net в iframe на news-site.com). Позволяют отслеживать перемещения между сайтами. На 2025 год полностью заблокированы по умолчанию в Safari и Firefox; в Chrome — отложено до конца 2024, но уже деактивированы в тестовых режимах.

  • localStorage / IndexedDB третьих лиц — использовались как «замена кукам» (supercookies). Браузеры ввели изоляцию по first-party (partitioning): данные, записанные в example.com через iframe с tracker.com, хранятся отдельно от данных, записанных при прямом заходе на tracker.com.

  • Fingerprinting (сбор отпечатка) — определение уникального профиля через:

    • Canvas API (рендеринг текста/изображений с небольшими вариациями);
    • WebGL (идентификаторы GPU, расширения);
    • Аудио API (характеристики синтеза звука);
    • Списки шрифтов (document.fonts);
    • Временные задержки операций (тайминг-атаки).

Современные браузеры применяют противодействие:

  • Стандартизация отчётов (например, navigator.userAgentData);
  • Округление значений (разрешение экрана, времени);
  • Блокировка нетипичных API в скрытых вкладках;
  • Предупреждения о попытках fingerprinting (в режиме разработчика).

Механизмы защиты

Content Security Policy (CSP)

CSP — HTTP-заголовок (Content-Security-Policy), задающий белый список доверенных источников для различных типов ресурсов: скриптов, стилей, шрифтов, фреймов, подключений. Пример политики:

Content-Security-Policy: default-src 'self'; script-src 'self' https://cdn.example.com; img-src * data:

Это означает:

  • Все ресурсы по умолчанию загружаются только с того же origin ('self');
  • Скрипты — только с origin и https://cdn.example.com;
  • Изображения — откуда угодно, включая data: URI.

CSP эффективно предотвращает XSS: даже если злоумышленник внедрит <script src="https://evil.com/steal.js">, браузер его не выполнит, если evil.com не в белом списке.

Same-Origin Policy и CORS

Same-Origin Policy (SOP) — фундаментальный принцип безопасности: скрипт с site-a.com не может читать ответ от site-b.com через fetch() или XMLHttpRequest, даже если запрос технически выполнен. Это изолирует источники.

CORS (Cross-Origin Resource Sharing) — механизм, расширяющий SOP по инициативе сервера. Если сервер site-b.com хочет разрешить доступ с site-a.com, он включает в ответ заголовки:

Access-Control-Allow-Origin: https://site-a.com
Access-Control-Allow-Credentials: true

Только тогда браузер передаст данные в JavaScript. Важно: CORS не блокирует отправку запроса — он блокирует доступ к ответу. Поэтому критичные операции (изменение данных) всегда должны проверять не только CORS, но и CSRF-токены.

Redirect Tracking Protection

Как отмечалось ранее, цепочки переадресаций (t.co → bit.ly → example.com) используются для незаметного трекинга. Браузер может перехватывать такие цепочки и:

  • Очищать URL от параметров вроде ?utm_source=...&fbclid=...;
  • Блокировать установку кук в промежуточных звеньях;
  • Применять к ним политики изоляции, как к third-party.

Например, в Safari включена функция Intelligent Tracking Prevention (ITP), которая анализирует граф переходов и применяет эвристики для подавления скрытого трекинга.

Private State Tokens (ранее Trust Tokens)

Это механизм борьбы с ботами и мошенничеством без отслеживания. Принцип:

  1. Пользователь посещает доверенный сайт (например, Google), проходит проверку (капча, поведенческий анализ).
  2. Сайт выдаёт анонимный токен — криптографический признак «этот пользователь, вероятно, человек», без привязки к личности.
  3. При переходе на партнёрский сайт (например, банк), токен передаётся в заголовке Sec-Redeem-Token.
  4. Партнёрский сайт может подтвердить подлинность токена через issuer, но не может использовать его для идентификации или профилирования.

Токены одноразовые, не поддаются перехвату и не связывают сеансы между сайтами. Это альтернатива кукам в сценариях anti-abuse.

Topics API (преемник FLoC)

FLoC (Federated Learning of Cohorts) была отвергнута из-за рисков: даже анонимные когорты могли быть уникальны для малых групп (например, «1 человек в Уфе, интересующийся ядерной физикой»), что позволяло деанонимизировать.

Topics API — более безопасная замена. Принцип:

  • Браузер (локально!) анализирует историю посещений за последнюю неделю;
  • На основе доменов определяет до 5 тем (например, «Технологии», «Спорт», «Путешествия») по заранее согласованному иерархическому таксону;
  • При запросе рекламы сайт может запросить одну тему из последних трёх недель (рандомизированно);
  • Тема передаётся в заголовке Sec-CH-Prefers-Topics;
  • Никаких идентификаторов, никакой передачи истории — только один обобщённый тег.

Пользователь может отключить Topics API в настройках. Все темы — общедоступные, не создаваемые динамически, что исключает создание «уникальных» когорт.


Сравнительный анализ механизмов хранения

Для осознанного проектирования архитектуры важно не просто знать доступные инструменты, но и понимать, в каких условиях каждый из них проявляет свои сильные и слабые стороны. Ниже приведено сравнение ключевых характеристик основных механизмов клиентского хранения по пяти критериям: ёмкость, срок жизни, доступность между контекстами, производительность и безопасность.

Ёмкость и лимиты

МеханизмТипичный лимит (на origin)Примечания
localStorage / sessionStorage5–10 МБЖёстко задано в браузере; превышение вызывает QuotaExceededError.
IndexedDBДо 60% свободного диска (Chrome), минимум 10 ГБДинамическая квота; можно запросить увеличение через navigator.storage.persist().
Cache StorageОбщая с IndexedDB квотаУправление — на уровне именованных кэшей; caches.delete() освобождает место.
Cookies4 КБ на куку, ≤ 50 кук на домен, ≤ 8 КБ суммарноОграничения на уровне HTTP; сервер не получит куки, превышающие лимит.
File System AccessНеограничено (пока есть место на диске)Права выдаются пользователем индивидуально на файл/директорию.
Extension Storagelocal: ~5 МБ; sync: ~100 КБСинхронизация подчиняется политикам аккаунта браузера.

Срок жизни данных

МеханизмСохраняется после перезагрузки страницыСохраняется после закрытия вкладки/браузераТребует ручной очистки
localStorageДаДаДа (через UI или API)
sessionStorageДаНет (удаляется при закрытии вкладки)Нет
IndexedDBДаДаДа
Cache StorageДаДаДа
CookiesMax-Age)Зависит от TTLДа, если Max-Age > сессияДа (браузер может сократить TTL при политике приватности)
Cookies (сессионные)ДаНет (удаляются при завершении процесса браузера)Нет
File System AccessНет (хэндлы утеряны)Нет
Примечание: хэндлы (FileSystemHandle) можно сохранить в IndexedDB — тогда доступ восстанавливается после перезагрузки приложения при условии, что файл не был удалён или перемещён.

Доступность между контекстами

МеханизмДоступен из iframe (same-origin)Доступен из iframe (cross-origin)Доступен из Service WorkerДоступен из Web Worker
localStorageДаНет (SOP)НетНет
sessionStorageДаНетНетНет
IndexedDBДаНетДаДа
Cache StorageДаНетДаДа
CookiesДа (автоматически)Нет (если SameSite=Strict/Lax)Да (через fetch)Да (через fetch)
File System AccessДа (если разрешено)НетНетНет

Обратите внимание: IndexedDB и Cache Storage — единственные хранилища, доступные из Service Worker, что критично для реализации офлайн-логики.

Производительность

МеханизмТип доступаБлокирует поток?Задержка (типично)Подходит для частых операций?
localStorageСинхронныйДаНизкая (ns–мкс)Нет (блокировки при > 1 КБ)
sessionStorageСинхронныйДаНизкаяНет
IndexedDBАсинхронныйНетСредняя (мс)Да (транзакции, пакетная запись)
Cache StorageАсинхронныйНетНизкая–средняяДа
CookiesСинхронный (для document.cookie)Да (в JS)Низкая (но сетевая задержка при передаче)Нет (сетевая накладная)
File System AccessАсинхронныйНетВысокая (диск/SSD)Только для редких, объёмных операций

Важно: синхронные операции (localStorage, document.cookie) могут вызывать «зависание» интерфейса при частом доступе, особенно на мобильных устройствах. Рекомендуется использовать их только для инициализации или редких событий.

Безопасность и приватность

МеханизмДоступен из JS?Передаётся в запросах?Подвержен XSS?Подвержен CSRF?Изолирован в приватном режиме?
localStorageДаНетДаНетДа
sessionStorageДаНетДаНетДа
IndexedDBДаНетДаНетДа
Cache StorageДаНет (но может содержать куки в Response)УсловноНетДа
Cookies (HttpOnly)НетДа (автоматически)НетДаДа
Cookies (без HttpOnly)ДаДаДаДаДа
File System AccessДа (через handle)НетНет (требует user gesture)НетДа (но хэндлы не сохраняются)

Ключевой вывод: никогда не храните токены аутентификации в localStorage или sessionStorage. При компрометации XSS-уязвимости злоумышленник мгновенно получит доступ к аккаунту. Единственно безопасное место для токена сессии — кука с флагами HttpOnly, Secure, SameSite=Strict/Lax.


Рекомендации по выбору технологии

Выбор механизма хранения должен быть продиктован функциональными и нефункциональными требованиями. Ниже — руководство по принятию решения.

Требуется сохранить небольшие настройки (язык, тема, флаги)?

→ Используйте localStorage.
Просто, надёжно, поддерживается везде. Убедитесь, что данные не содержат чувствительной информации.

Требуется временно сохранить состояние вкладки (шаг многостраничной формы, черновик)?

sessionStorage.
Автоматическая очистка при закрытии вкладки снижает риск утечки.

Требуется офлайн-режим, работа с большими объёмами структурированных данных?

IndexedDB (с библиотекой-обёрткой, например Dexie.js).
Это стандарт де-факто для сложных клиентских приложений: почтовые клиенты, редакторы, PWA.

Требуется кэширование сетевых ресурсов (JS, CSS, API-ответы) для ускорения или офлайн-доступа?

Cache Storage в связке с Service Worker.
Комбинируйте стратегии: stale-while-revalidate для API, cache-first для статики.

Требуется работать с пользовательскими файлами (редактор кода, графический редактор)?

File System Access API (с fallback на input[type=file] + Blob для старых браузеров).
Обеспечьте graceful degradation: если API недоступен, используйте загрузку/выгрузку через Blob URL.

Требуется синхронизация между устройствами (настройки расширения)?

chrome.storage.sync (для расширений) или облачное API (Firebase, собственный backend) для веб-приложений.
IndexedDB + периодическая синхронизация — распространённый паттерн для PWA.

Требуется хранить токен аутентификации?

HttpOnly-кука с Secure, SameSite=Strict.
Если SPA требует доступа к токену (например, для заголовка Authorization), используйте комбинированный подход:
— Идентификатор сессии в HttpOnly-куке (для аутентификации);
— Небольшой, подписанный JWT с правами в localStorage (только для UI-логики, не для аутентификации на сервере).

Требуется высокая производительность записи (логи, метрики)?

IndexedDB в режиме readonly/readwrite с пакетной записью или Service Worker + Background Sync.
Не пишите по одной записи — накапливайте буфер и отправляйте пакетами.


Угрозы и практики безопасного хранения

Несмотря на изоляцию, клиентское хранилище — среда с высоким уровнем риска. Пользователь имеет полный контроль над своим браузером, и любые данные, попавшие на клиент, могут быть прочитаны, изменены или удалены. Поэтому:

  1. Никогда не храните на клиенте то, что не должно там быть:
    — Пароли, приватные ключи, персональные данные без шифрования;
    — Бизнес-логику, которую можно извлечь и скопировать (например, алгоритмы ценообразования);
    — Неограниченные токены с правами администратора.

  2. Всегда проверяйте данные на сервере.
    Клиент — недоверенная сторона. Даже если данные подписаны (JWT), сервер должен валидировать их срок, права и контекст использования.

  3. Используйте шифрование на клиенте только для защиты от случайного доступа (например, локальное шифрование заметок паролем).
    Положитесь на Web Crypto API (SubtleCrypto), но помните: ключ хранится на том же устройстве, а значит, шифрование не защищает от целенаправленной атаки на устройство.

  4. Ограничивайте права Service Worker.
    Service Worker имеет доступ к Cache и IndexedDB, может перехватывать все запросы. Убедитесь, что его код минимален, подписан (Subresource Integrity), и обновляется по безопасному каналу.

  5. Отслеживайте изменения в политике приватности браузеров.
    Уже сегодня Safari блокирует доступ к document.referrer в кросс-сайтовых контекстах, Chrome ограничивает User-Agent, Firefox вводит :has() в CSP. То, что работает сегодня, может перестать работать завтра.