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

Хранение данных в браузере и на сервере

Всем

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

Play ITЗагрузка интерактивного демо…

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

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

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


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

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

  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 Данные) и куки (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-запросе, что увеличивает объём трафика, особенно при множестве мелких ресурсов.

Cookie (Set-Cookie)localStorage
Кто задаётСервер в ответеJavaScript на странице
Отправка на серверАвтоматически в заголовке CookieТолько если JS сам положит в fetch
HttpOnlyДа — защита от XSS на чтение токенаНет
Типичное применениеСессия, CSRF-токен, предпочтения с серверомНастройки UI, черновики, кэш JSON
Лимит~4 КБ на cookie~5–10 МБ на origin

Сессионный идентификатор с флагами Secure; HttpOnly; SameSite=Lax — типичный паттерн для входа в аккаунт; произвольные секреты в localStorage уязвимы при XSS.

В рунет-дискурсе​

В бытовой речи cookie часто смешивают с "печеньем" и шутят про баннеры "мы используем куки" — это отсылка к GDPR и российским требованиям к уведомлению, а не к десерту. В форумах 2010-х third-party cookies ассоциировали с "слежкой"; сегодня браузеры режут их по умолчанию, а споры перешли к рекламным ID и fingerprinting.

Для учёбы важно различать технический механизм (заголовки Set-Cookie / Cookie) и политику сайта (какие данные собирают и зачем). Иронические формулировки — Неолурк (Cookies); разбор площадок и лексики — Форумная культура Рунета. Cookie, session, JWT и PASETO — аутентификация.


Cookies и sessions — два подхода к состоянию​

Cookie и session часто произносят в одной фразе, но это разные уровни:

  • Cookie — HTTP-механизм — браузер хранит имя=значение и шлёт его серверу (настройки, Session ID, CSRF-токен).
  • Session — серверный паттерн: состояние пользователя в Session Store, клиенту достаётся только идентификатор (часто в cookie).

JWT и PASETO — уже форматы токена для stateless-проверки; их кладут в cookie или передают в Authorization: Bearer. Полное сравнение четырёх подходов — в аутентификации и авторизации.

При входе в аккаунт и последующих запросах страниц используются два разных способа "помнить" пользователя через cookie. Оба передают состояние через HTTP cookie, но размещают данные в разных местах.

Подход с данными в cookie (client-side). Токен или сериализованные поля сессии хранятся на клиенте — внутри cookie.

Вход:

  1. Браузер отправляет логин и пароль на сервер.
  2. Сервер проверяет учётные данные и отвечает заголовком Set-Cookie, в котором cookie содержит сам токен или данные пользователя (часто подписанные или зашифрованные).
  3. Браузер сохраняет cookie локально.

Обращение к странице:

  1. При каждом запросе браузер автоматически прикладывает cookie в заголовок Cookie.
  2. Сервер читает данные прямо из cookie и формирует ответ — обращение к Session Store не требуется.

Подход с Session ID (server-side). На клиенте хранится только идентификатор; реальные данные сессии лежат в Session Store на сервере — память процесса, Redis, Memcached или таблица в БД.

Вход:

  1. Браузер отправляет логин и пароль на сервер.
  2. Сервер создаёт запись в Session Store и отвечает Set-Cookie, где cookie содержит только Session ID.
  3. Браузер сохраняет короткий идентификатор.

Обращение к странице:

  1. Браузер снова автоматически отправляет cookie с Session ID.
  2. Сервер по ID запрашивает данные у Session Store.
  3. Session Store возвращает состояние пользователя (роли, корзина, время входа).
  4. Сервер формирует ответ с учётом этих данных.
КритерийДанные в cookieSession ID + Session Store
Где лежат данные сессииНа клиенте (в cookie)На сервере
Что уходит в каждом запросеТокен или payload целикомКороткий идентификатор
Серверное хранилищеОбычно не нужноОбязательно (Redis, БД, память)
Простота развёртыванияВыше — сервер statelessНиже — нужно масштабировать store
БезопасностьНиже — данные доступны клиентуВыше — секреты остаются на сервере
Отзыв сессииСложнее — cookie остаётся валидным до истеченияПроще — удалить запись в store

Третий распространённый вариант — JWT в cookie или в заголовке Authorization: Bearer …. Сервер после входа подписывает claims (sub, exp, роли) и отдаёт строку header.payload.signature. При следующих запросах достаточно проверить подпись — Session Store для самой аутентификации не нужен. Минус — сложнее отозвать токен до exp; плюс — проще масштабировать API без общего Redis.

Краткий поток:

  1. Логин → сервер проверяет пароль.
  2. Сервер создаёт и подписывает JWT.
  3. Клиент хранит JWT (HttpOnly cookie или secure storage).
  4. Запрос с Authorization: Bearer <token> → сервер валидирует подпись и срок.
  5. Сервер возвращает данные.

PASETO решает ту же задачу, что JWT, но фиксирует версию и тип криптографии (v2.local, v2.public) вместо свободного поля alg. Подробнее — Cookies, sessions, JWT и PASETO.

Когда что выбирать

Cookie с токеном или подписанным payload удобны для stateless API и горизонтального масштабирования без общего хранилища. Session ID + Session Store — типичный выбор для классических веб-приложений с входом в аккаунт: проще отозвать сессию, меньше секретов у клиента. Сводная таблица cookie / session / JWT / PASETO, флаги HttpOnly, Secure, SameSite, CSRF и эволюция Basic → OAuth — в аутентификации и авторизации.

Ниже — те же два подхода с акцентом на реализацию.

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

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


Кэш​

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

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

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

  2. Кэш Service Worker (Cache Storage) — программно управляемый кэш на уровне приложения. Позволяет реализовать сложные стратегии — cache-first, Сеть-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 * Данные:

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

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

CSP эффективно предотвращает XSS (отражённый, сохранённый и DOM-based — см. три типа): даже если злоумышленник внедрит <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ДаДаДа
Cookies (с Max-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)НетДа (но хэндлы не сохраняются)

Ключевой вывод: никогда не храните долгоживущие токены (refresh, session ID) в localStorage или sessionStorage. При XSS злоумышленник прочитает их из DOM-хранилища. Для SPA с JWT типичная схема — access — только в памяти приложения, refresh — в cookie с HttpOnly, Secure, SameSite=Strict/Lax. Пошаговые потоки login, refresh и logout — в аутентификации и авторизации.


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

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


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

→ Используйте 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-кука с Session ID и Secure, SameSite=Strict (или Lax).
→ Для SPA + JWT — короткий access в памяти (заголовок Authorization), refresh только в HttpOnly-куке; ротация refresh и logout через blacklist — безопасный поток входа.
Не кладите refresh в localStorage; access в localStorage допустим только если команда осознанно принимает риск XSS и компенсирует его коротким exp и жёстким CSP — для новых проектов предпочтительнее память процесса.


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

→ 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. То, что работает сегодня, может перестать работать завтра.


Основа по протоколу

Базовый разбор HTTP и HTTPS находится в отдельной статье — HTTP как основа веб-интеграций.


Практика хранения — что где держать​

Быстрый выбор хранилища​

  • Настройки интерфейса -> localStorage.
  • Крупные структуры и офлайн-данные -> IndexedDB.
  • Кэш ответов и статики -> Cache Storage.
  • Сессионный идентификатор -> HttpOnly cookie.

Типовые ошибки​

  • Хранение access-токена в localStorage при XSS-риске.
  • Использование localStorage для больших массивов данных.
  • Отсутствие стратегии очистки устаревшего кэша.

См. также​