Идентичность

Доступ к хранилищу для встроенного контента

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

Документация

Нужна структурированная документация по теме Идентичность?

Эта статья относится к редакционной библиотеке. Для пошаговой настройки, справки и постоянных обновлений переходите сразу в соответствующий раздел docs.

Storage Access API позволяет встроенному документу попросить у браузера доступ к его неразделённым cookies, а в некоторых браузерах и к другим данным хранилища, в контексте, где такой доступ иначе заблокирован или разделён. Встроенный документ проверяет текущее состояние через document.hasStorageAccess() и запрашивает доступ через document.requestStorageAccess(). Результат определяет браузер, а не страница, обычно после жеста пользователя и иногда после окна подтверждения. Предоставленный доступ является узким исключением для одного встроенного документа на одном сайте верхнего уровня, а странице нужен заранее продуманный путь для отклонённых запросов и для браузеров без поддержки.

Что решает Storage Access API

Многие браузеры разделяют или блокируют состояние, к которому может обратиться встроенный сторонний документ. Встроенный виджет входа, платёжная форма, сервис комментариев или чат поддержки могут обнаружить, что cookies, установленные во время визита пользователя на их собственный сайт, не отправляются из страницы другого сайта. Storage Access API является стандартизованным способом для такого документа спросить, можно ли снова сделать это состояние доступным. Модель описывают обзор Storage Access API на MDN и черновик спецификации PrivacyCG.

Схема: встроенный документ проверяет состояние через hasStorageAccess, запрашивает доступ через requestStorageAccess после жеста пользователя и получает результат: разрешено, отклонено или не поддерживается, для каждого предусмотрен свой путь

Главное слово здесь: запрос. Встроенный документ просит, а не настраивает. Браузер применяет собственные правила: разрешён ли запрос, нужно ли спрашивать пользователя и какое предварительное взаимодействие у пользователя было со встроенным сайтом. Эти правила различаются по браузерам и версиям и могут меняться между выпусками. Страница может описать, о чём она просит, но не может обещать, что увидит пользователь.

Решение пользователя и есть причина, по которой API устроен именно так. Человек, который открывает сайт со встроенным сторонним сервисом, не обязательно хотел делиться с этим сайтом сохранённым состоянием сервиса. Поэтому браузер спрашивает: либо через окно подтверждения, где названы встроенный сайт и страница, на которой он показан, либо по собственным правилам о предварительном взаимодействии. Предоставленный доступ фиксирует выбор пользователя, а не предпочтение страницы.

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

Ключи разделения, то есть механика, с помощью которой браузеры разделяют состояние по сайту верхнего уровня, разобраны в материале Разделение хранилища браузера и конфиденциальность. Storage Access API опирается на эту границу как исключение и не заменяет её. Более широкий вопрос о том, какой механизм хранения подходит для каких данных, рассмотрен в материале Cookies, localStorage и IndexedDB: где должно жить состояние.

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

Проверка состояния и запрос доступа

document.hasStorageAccess() возвращает промис, который разрешается булевым значением для текущего документа. Метод читает текущее состояние и ничего не запрашивает, поэтому его можно вызывать при загрузке без жеста пользователя. Результат true означает, что сейчас у документа есть доступ к его неразделённым cookies. Причиной может быть доступ, предоставленный раньше, то, что документ не встроен, или то, что в этой конфигурации браузер не ограничивает сторонние cookies, поэтому одно значение true не доказывает, что доступ был предоставлен.

document.requestStorageAccess() и есть сам запрос. Он тоже возвращает промис, который разрешается при предоставлении доступа и отклоняется в противном случае. Нужна кратковременная активация пользователем, например щелчок или нажатие клавиши внутри встроенного документа, поэтому вызов из таймера или при загрузке, как ожидается, будет отклонён, если только разрешение не было предоставлено раньше: в этом случае некоторые браузеры разрешают вызов без жеста. Вызов может быть отклонён и тогда, когда пользователь отказался в окне подтверждения, когда правила браузера не допускают запрос или когда запомнен прежний отказ. Относитесь к отклонению как к обычному исходу, а не как к исключению, которое нужно скрыть. Условия перечислены в справке по requestStorageAccess().

Для большинства встроенных виджетов подходит короткая последовательность. При загрузке проверьте наличие методов и вызовите hasStorageAccess(). Если результат true, продолжайте обычным путём для вошедшего пользователя. Если false, покажите элемент, который объясняет, что нужно виджету, и вызывайте requestStorageAccess() только из обработчика щелчка по этому элементу. После разрешённого промиса перезагрузите или заново запросите состояние, зависящее от cookies, и снова подтвердите результат через hasStorageAccess(), прежде чем показывать содержимое для вошедшего пользователя.

async function showAccountState(button) {
  if (!('hasStorageAccess' in document)) return renderSignedOut('unsupported');
  if (await document.hasStorageAccess()) return renderSignedIn();
  button.hidden = false;
  button.addEventListener('click', async () => {
    try {
      await document.requestStorageAccess();
      return (await document.hasStorageAccess()) ? renderSignedIn() : renderSignedOut('denied');
    } catch {
      return renderSignedOut('denied');
    }
  });
}

При следующих визитах снова вызывайте hasStorageAccess(), а не считайте, что прежнее предоставление доступа ещё действует. Браузеры запоминают решения или отменяют их по собственному графику, пользователь может сбросить разрешения сайта, а профиль может быть очищен. Документ, который проверяет состояние при каждой загрузке, исходит из текущего ответа браузера и не переносит устаревшее предположение из одного визита в другой. Руководство по использованию Storage Access API показывает тот же шаблон подробнее.

Действуют и несколько условий на уровне страницы. Встроенному документу нужен защищённый контекст. Если фрейм находится в sandbox, страница, которая его встраивает, должна разрешить токен sandbox для доступа к хранилищу (allow-storage-access-by-user-activation) вместе с токенами, нужными скрипту, такими как allow-scripts и allow-same-origin. Политика Permissions Policy также может ограничить возможность storage-access для фрейма. Если запрос отклонён сразу, проверьте атрибуты фрейма и политику встраивающей страницы, прежде чем предполагать, что пользователь принял решение.

Если браузер это поддерживает, Permissions API сообщает состояние разрешения storage-access как «granted» или «prompt»; спецификация не раскрывает состояние «denied», поэтому отклонённый запрос читается как «prompt». Этот запрос позволяет прочитать состояние до обращения, но не изменить его. Поддержка такого запроса различается по браузерам, поэтому считайте его необязательным сигналом, а опираться при действиях продолжайте на hasStorageAccess().

Предоставленный доступ не переписывает правила cookies. Cookie, которая должна передаваться в межсайтовом контексте, нуждается в SameSite=None и Secure в браузерах, применяющих правила SameSite к запросу, а проверка должна фиксировать атрибуты, которые она наблюдает. Если виджет по-прежнему выглядит так, будто вход не выполнен, хотя hasStorageAccess() возвращает true, проверьте атрибуты cookie и источник запроса, прежде чем подозревать сам API.

Различия между браузерами и поддержка

Поведение браузеров является той частью темы, которая меняется чаще всего. Браузеры на основе Chromium, Firefox и Safari поддерживают API, но различаются тем, когда появляется окно подтверждения, важно ли предшествующее взаимодействие со встроенным сайтом как со страницей верхнего уровня, как долго запоминается решение и какое хранилище охватывает предоставленный доступ. Некоторые браузеры также применяют собственные эвристики или связи между сайтами, которые могут предоставить или отклонить доступ без видимого окна. Это решения браузера, и скрипт страницы не может их навязать.

Считайте поддержку величиной, которую измеряют для каждого браузера и версии. Проверка возможности вроде 'requestStorageAccess' in document говорит лишь о том, что метод существует. Она не говорит, покажет ли вызов окно подтверждения, разрешится ли молча или будет отклонён. Сверяйтесь с таблицами совместимости MDN и рекомендациями производителей, например с руководством Chrome по Storage Access API, и записывайте протестированную версию, потому что одна и та же страница после обновления браузера может вести себя иначе.

Браузеры различаются и исходным отношением к сторонним cookies. Одни блокируют или разделяют их по умолчанию, другие оставляют выбор пользователю, а корпоративная политика может снова изменить результат. В браузере, который не ограничивает сторонние cookies, hasStorageAccess() может вернуть true без какого-либо запроса. В браузере, который их ограничивает, той же странице запрос нужен. Результат теста в одной конфигурации не описывает другую, поэтому в записи проверки указывают браузер и его параметры.

Встроенный сайт и сайт верхнего уровня оба входят в решение. Пользователь может увидеть окно, где названы оба, а предоставление доступа для одной пары ничего не говорит о другой. Если один и тот же виджет показывается на нескольких ваших сайтах, рассчитывайте тестировать и объяснять каждую пару и учитывайте, что пользователи увидят запрос не один раз.

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

Проектирование путей для отказа и отсутствия поддержки

Три исхода требуют продуманного пути: разрешено, отклонено и не поддерживается. «Разрешено» продолжается обычным состоянием для вошедшего пользователя. «Отклонено» означает, что пользователь отказался, браузер отклонил вызов или запомнен прежний отказ. «Не поддерживается» означает, что методов нет или браузер обрабатывает возможность иначе. Каждому из двух последних нужно видимое работающее состояние, а не пустой фрейм и не баннер с ошибкой.

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

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

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

Не стройте решение вокруг окна подтверждения. Страница не может ответить на него, скрыть или подавить его и не должна пытаться вызвать его вне настоящего действия пользователя. Не описывайте окно пользователям как обязательное для работы сервиса, если существует путь через контекст первой стороны. Формулировка и момент появления окна принадлежат браузеру. Задача страницы: показать перед щелчком пояснение о том, что сможет прочитать встроенный сервис и зачем.

Используйте определение возможностей и обработку сбоев вместо проверок user agent. Ветвитесь по наличию методов и по результату промиса, а не по названию или строке версии браузера, чтобы обновление, добавляющее или меняющее поддержку, не требовало правок кода. Записывайте для собственной диагностики категорию исхода, например разрешено, отклонено, не поддерживается или отклонено без жеста, и не включайте в записи значения cookies и идентификаторы аккаунтов.

Проверка собственного встроенного виджета

Тестируйте API на своём виджете, встроенном в тестовый сайт верхнего уровня, который тоже принадлежит вам, чтобы вы управляли обоими источниками и cookies. Используйте два разных регистрируемых домена или два локальных имени хоста, которые браузер считает разными сайтами, чтобы фрейм был действительно межсайтовым. Тестовая страница на одном поддомене, встраивающая другой поддомен того же регистрируемого домена, относится к тому же сайту и не задействует ограничение.

Записывайте условия каждого запуска, а не предполагайте одинаковое поведение браузеров: название и версию браузера, сайт верхнего уровня, встроенный источник, атрибуты SameSite и Secure проверяемой cookie, находится ли iframe в sandbox или имеет атрибут allow, предшествовал ли вызову жест пользователя, наблюдаемый результат hasStorageAccess(), исход вызова и, если доступно, состояние разрешения. Два запуска с разными записями являются разными экспериментами.

Начинайте каждый запуск с известного состояния cookies. В материале об управлении cookies описано, как cookies предварительно загружаются для сеанса браузера, а в материале об изоляции аккаунтов в браузере описано, как держать контексты раздельно. Чистое исходное состояние здесь важно, потому что cookie, оставшаяся от прошлого запуска, может заставить отклонённый путь выглядеть как разрешённый.

BotBrowser поддерживает --bot-cookies, который внедряет cookies при запуске или для каждого BrowserContext, поэтому каждый тестовый контекст может начинать с собственного документированного состояния cookies, пока проверяется принадлежащий вам встроенный сценарий. BotBrowser не предоставляет и не отклоняет запросы Storage Access API, не отвечает на окна подтверждения браузера и не подавляет их, а также не изменяет, каким встроенным документам браузер разрешает использовать неразделённое хранилище; эти исходы остаются за браузером и пользователем.

Выполняйте случай с предоставлением доступа с настоящим жестом пользователя и настоящим решением в тестируемом браузере. Если на окно подтверждения нельзя ответить в автоматизированном запуске, выполните этот случай вручную и запишите его как ручной результат. Не подменяйте предоставление доступа внедрённой cookie: это проверило бы экран для вошедшего пользователя и ничего не сказало бы о запросе.

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

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

Выполните проверки доступа к хранилищу

Выполните эти проверки на принадлежащем вам встроенном виджете в каждом поддерживаемом вами браузере и версии и запишите для каждой «пройдено» или «не пройдено».

  1. Состояние и запрос. Пройдено, если страница вызывает hasStorageAccess() при загрузке без окна подтверждения, вызывает requestStorageAccess() только из обработчика щелчка, а запись показывает оба результата раздельно. Не пройдено, если запрос выполняется при загрузке или два результата смешаны.
  2. Жест и отклонение. Начав с состояния без прежнего предоставления доступа, вызовите запрос один раз без жеста пользователя и один раз с жестом. Пройдено, если первый вызов обработан как отклонение с видимым состоянием без входа, а второй сообщает свой настоящий исход.
  3. Путь с предоставленным доступом. После результата «разрешено» убедитесь, что hasStorageAccess() возвращает true, а виджет показывает состояние для вошедшего пользователя с ожидаемой cookie. Не пройдено, если состояние для вошедшего пользователя появляется при hasStorageAccess() равном false.
  4. Путь с отказом. Откажитесь в окне подтверждения или используйте конфигурацию, где вызов отклоняется. Пройдено, если виджет показывает определённый запасной путь через контекст первой стороны или состояние без входа, сохраняет публичное содержимое и введённые черновики и сам не запрашивает доступ повторно.
  5. Путь без поддержки. В браузере или конфигурации, где методов нет, пройдено, если виджет приходит к тому же определённому запасному пути через определение возможностей и не ветвится по названию браузера.
  6. Запись об окружении. Пройдено, если запись содержит браузер и версию, сайт верхнего уровня, встроенный источник, атрибуты SameSite и Secure, параметры sandbox и allow и наблюдаемое состояние разрешения. Не пройдено, если результат сообщён без них или предполагается, что он переносится на другой браузер.
  7. Область действия доступа. Встройте тот же виджет на втором тестовом сайте верхнего уровня. Пройдено, если этот сайт получает собственный результат, а предоставление доступа на первом не считается действующим.

Источники

#Api Доступа К Хранилищу#Встроенный Контент#Cookies#Конфиденциальность Браузера#Разделение Хранилища

Переведите BotBrowser из исследований в продакшн

Используйте эти руководства, чтобы понять модель, а затем перейти к кроссплатформенной валидации, изолированным контекстам и масштабируемому браузерному развертыванию.