Краткое руководство по внедрению общего хранилища и частного агрегирования

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

Целевая аудитория: компании, занимающиеся рекламными технологиями и аналитикой.

API общего хранилища

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

Использование общего хранилища ограничено источником контекста (вызывающей стороной метода sharedStorage ).

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

Вызов общего хранилища

Специалисты по рекламным технологиям могут записывать данные в Shared Storage, используя JavaScript или заголовок ответа. Чтение из Shared Storage происходит только в изолированной среде JavaScript, называемой worklet.

  • С помощью JavaScript специалисты по рекламным технологиям могут выполнять определенные функции общего хранилища, такие как установка, добавление и удаление значений, вне JavaScript-ворлета. Однако такие функции, как чтение из общего хранилища и выполнение частной агрегации, должны выполняться через JavaScript-ворлет. Методы, которые можно использовать вне JavaScript-ворлета, можно найти в разделе «Предлагаемый интерфейс API — вне ворлета» .

    Методы, используемые в рабочем модуле во время операции, можно найти в разделе «Предлагаемый API-интерфейс — в рабочем модуле» .

  • Использование заголовков ответа

    Подобно JavaScript, в Shared Storage с помощью заголовков ответа можно выполнять только определенные функции, такие как установка, добавление и удаление значений. Для работы с Shared Storage в заголовке ответа необходимо включить Shared-Storage-Writable: ?1 .

    Для инициирования запроса от клиента выполните следующий код, в зависимости от выбранного вами метода:

    • Использование функции fetch()

      fetch("https://a.example/path/for/updates", {sharedStorageWritable: true});
      
    • Использование тега iframe или img

      <iframe src="https://a.example/path/for/updates" sharedstoragewritable></iframe>
      
    • Использование атрибута IDL с тегом iframe или img

      let iframe = document.getElementById("my-iframe");
      iframe.sharedStorageWritable = true;
      iframe.src = "https://a.example/path/for/updates";
      

Дополнительную информацию можно найти в разделе «Общее хранилище: заголовки ответа» .

Запись в общее хранилище

Для записи в Shared Storage вызовите sharedStorage.set() внутри или вне JavaScript-ворлета. Если вызов производится вне ворлета, данные записываются в источник контекста просмотра, из которого был сделан вызов. Если вызов производится внутри ворлета, данные записываются в источник контекста просмотра, который загрузил ворлет. Установленные ключи имеют срок действия 30 дней с момента последнего обновления.

Поле ignoreIfPresent является необязательным. Если оно присутствует и установлено в true , ключ не обновляется, если он уже существует. Срок действия ключа продлевается на 30 дней с момента вызова set() даже если ключ не обновлялся.

Если обращение к общему хранилищу происходит несколько раз при загрузке одной и той же страницы с использованием одного и того же ключа, значение ключа перезаписывается. Рекомендуется использовать sharedStorage.append() если необходимо сохранить предыдущее значение ключа.

  • Использование JavaScript

    Вне рабочего модуля:

    window.sharedStorage.set('myKey', 'myValue1', { ignoreIfPresent: true });
    // Shared Storage: {'myKey': 'myValue1'}
    window.sharedStorage.set('myKey', 'myValue2', { ignoreIfPresent: true });
    // Shared Storage: {'myKey': 'myValue1'}
    window.sharedStorage.set('myKey', 'myValue2', { ignoreIfPresent: false });
    // Shared Storage: {'myKey': 'myValue2'}
    

    Аналогично, внутри рабочего модуля:

    sharedStorage.set('myKey', 'myValue1', { ignoreIfPresent: true });
    
  • Использование заголовков ответа

    Вы также можете записывать данные в Shared Storage, используя заголовки ответа. Для этого укажите Shared-Storage-Write в заголовке ответа вместе со следующими командами:

    Shared-Storage-Write : set;key="myKey";value="myValue";ignore_if_present
    
    Shared-Storage-Write : set;key="myKey";value="myValue";ignore_if_present=?0
    

    Несколько элементов могут быть разделены запятыми и могут сочетать в себе set , append , delete и clear .

    Shared-Storage-Write :
    set;key="hello";value="world";ignore_if_present, set;key="good";value="bye"
    

Добавление значения

Вы можете добавить значение к существующему ключу, используя метод `append`. Если ключ не существует, вызов метода append() создаст ключ и установит его значение. Это можно сделать с помощью JavaScript или заголовка ответа.

  • Использование JavaScript

    Для обновления значений существующих ключей используйте sharedStorage.append() как внутри, так и вне рабочего модуля.

    window.sharedStorage.append('myKey', 'myValue1');
    // Shared Storage: {'myKey': 'myValue1'}
    window.sharedStorage.append('myKey', 'myValue2');
    // Shared Storage: {'myKey': 'myValue1myValue2'}
    window.sharedStorage.append('anotherKey', 'hello');
    // Shared Storage: {'myKey': 'myValue1myValue2', 'anotherKey': 'hello'}
    

    Для добавления внутрь рабочего модуля:

    sharedStorage.append('myKey', 'myValue1');
    
  • Использование заголовков ответа

    Аналогично установке значения в общем хранилище, вы можете использовать заголовок ответа Shared-Storage-Write для передачи пары ключ-значение.

    Shared-Storage-Write : append;key="myKey";value="myValue2"
    

Пакетное обновление значений

Вы можете вызвать sharedStorage.batchUpdate() как внутри, так и вне JavaScript-ворлета, передав упорядоченный массив методов, определяющих выбранные операции. Конструктор каждого метода принимает те же параметры, что и соответствующий отдельный метод для установки, добавления, удаления и очистки.

Вы можете вызвать batchUpdate() из JavaScript или использовать заголовок ответа:

  • Использование JavaScript

    К методам JavaScript, которые можно использовать с batchUpdate() относятся:

    • SharedStorageSetMethod() : Записывает пару ключ-значение в общее хранилище.
    • SharedStorageAppendMethod() : Добавляет значение к существующему ключу в общем хранилище или записывает пару ключ-значение, если ключ еще не существует.
    • SharedStorageDeleteMethod() : Удаляет пару ключ-значение из общего хранилища.
    • SharedStorageClearMethod() : Очищает все ключи в общем хранилище.
    sharedStorage.batchUpdate([
    new SharedStorageSetMethod('keyOne', 'valueOne'),
    new SharedStorageAppendMethod('keyTwo', 'valueTwo'),
    new SharedStorageDeleteMethod('keyThree'),
    new SharedStorageClearMethod()
    ]);
    
  • Использование заголовков ответа

    Shared-Storage-Write : set;key=keyOne;value=valueOne, append;key=keyTwo;value=valueTwo,delete;key=keyThree,clear
    

Использование заголовков ответа выполняет batchUpdate() для всех методов, указанных в заголовке.

Чтение из общей памяти

Чтение из общего хранилища возможно только внутри рабочего модуля.

await sharedStorage.get('mykey');

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

Удаление из общего хранилища

Удаление данных из общего хранилища можно выполнить с помощью JavaScript как внутри, так и вне рабочего процесса, либо используя заголовки ответа с помощью delete() . Для удаления всех ключей одновременно используйте clear() в любом из этих способов.

  • Использование JavaScript

    Для удаления из общего хранилища вне рабочего модуля:

    window.sharedStorage.delete('myKey');
    

    Чтобы удалить данные из общего хранилища (Shared Storage) изнутри рабочего процесса:

    sharedStorage.delete('myKey');
    

    Чтобы удалить все ключи сразу извне рабочего модуля:

    window.sharedStorage.clear();
    

    Чтобы удалить все ключи сразу из рабочего модуля:

    sharedStorage.clear();
    
  • Использование заголовков ответа

    Для удаления значений с помощью заголовков ответа можно также использовать заголовок Shared-Storage-Write чтобы передать ключ, который нужно удалить.

    delete;key="myKey"
    

    Чтобы удалить все ключи, используя заголовки ответа:

    clear;
    

Чтение групп интересов, защищенных авторским правом, из общего хранилища.

Вы можете считывать группы интересов Protected Audience из рабочего модуля Shared Storage. Метод interestGroups() возвращает массив объектов StorageInterestGroup , включая атрибуты AuctionInterestGroup и GenerateBidInterestGroup .

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

async function analyzeInterestGroups() {
  const interestGroups = await interestGroups();
  numIGs = interestGroups.length;
  maxBidCountIG = interestGroups.reduce((max, cur) => { return cur.bidCount > max.bidCount ? cur : max; }, interestGroups[0]);
  console.log("The IG that bid the most has name " + maxBidCountIG.name);
}

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

Параметры

Все методы-модификаторы Shared Storage поддерживают необязательный объект параметров в качестве последнего аргумента.

с замком

Параметр withLock является необязательным. Если он указан, то указывает методу получить блокировку для определенного ресурса с помощью API Web Locks, прежде чем продолжить. Имя блокировки передается при запросе блокировки. Это имя представляет собой ресурс, использование которого координируется в нескольких вкладках, рабочих процессах или коде внутри исходного узла.

Параметр withLock можно использовать со следующими методами модификации Shared Storage:

  • набор
  • добавить
  • удалить
  • прозрачный
  • пакетное обновление

Установить блокировку можно с помощью JavaScript или заголовка ответа:

  • Использование JavaScript

    sharedStorage.set('myKey', 'myValue', { withLock: 'myResource' });
    
  • Использование заголовков ответа

    Shared-Storage-Write : set;key="myKey";value="myValue",options;with_lock="myResource"
    

Блокировки в Shared Storage разделены по источнику данных. Эти блокировки независимы от любых блокировок, полученных с помощью метода `request() ` менеджера блокировки , независимо от того, находятся ли они в контексте window или worker . Тем не менее, они имеют ту же область видимости, что и блокировки, полученные с помощью request() в контексте SharedStorageWorklet .

Хотя метод request() позволяет использовать различные параметры конфигурации, блокировки, полученные в рамках общего хранилища, всегда соответствуют следующим настройкам по умолчанию:

  • mode: "exclusive" : Одновременное удержание других замков с тем же именем невозможно.
  • steal: false : Существующие блокировки с тем же именем не освобождаются для обработки других запросов.
  • ifAvailable: false : Запросы ожидают неопределенное время, пока блокировка не станет доступной.
Когда использовать withLock

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

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

Порядок замков

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

Например:

// This line might pause until the lock is available.
sharedStorage.set('keyOne', 'valueOne', { withLock: 'resource-lock' });

// This line will run right away, even if the first one is still waiting.
sharedStorage.set('keyOne', 'valueTwo');
Пример изменения нескольких клавиш

Опция withLock в методе batchUpdate() обеспечивает взаимное исключение с другими параллельными операциями, получающими ту же блокировку. Опцию withLock для batchUpdate() можно применить только ко всему пакету данных. Применение withLock к любому отдельному объекту метода в пакете данных вызовет исключение.

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

В следующем примере файла modify-multiple-keys.js устанавливаются новые значения для keyOne и keyTwo с помощью modify-lock а затем выполняется операция modify-multiple-keys из рабочего модуля:

// modify-multiple-keys.js
sharedStorage.batchUpdate([
    new SharedStorageSetMethod('keyOne', calculateValueFor('keyOne')),
    new SharedStorageSetMethod('keyTwo', calculateValueFor('keyTwo'))
], { withLock: 'modify-lock' });

const modifyWorklet = await sharedStorage.createWorklet('modify-multiple-keys-worklet.js');
await modifyWorklet.run('modify-multiple-keys');

Затем, в файле modify-multiple-keys-worklet.js вы можете запросить блокировку, используя navigator.locks.request() чтобы прочитать и изменить ключи по мере необходимости.

// modify-multiple-keys-worklet.js
class ModifyMultipleKeysOperation {
  async run(data) {
    await navigator.locks.request('modify-lock', async (lock) => {
      const value1 = await sharedStorage.get('keyOne');
      const value2 = await sharedStorage.get('keyTwo');

      // Do something with `value1` and `value2` here.

      await sharedStorage.delete('keyOne');
      await sharedStorage.delete('keyTwo');
    });
  }
}
register('modify-multiple-keys', ModifyMultipleKeysOperation);

Переключение контекста

Данные из общего хранилища записываются в источник (например, https://example.adtech.com) контекста просмотра, из которого был инициирован вызов.

При загрузке стороннего кода с помощью тега <script> , код выполняется в контексте просмотра встраиваемого приложения. Поэтому, когда сторонний код вызывает sharedStorage.set() , данные записываются в Shared Storage встраиваемого приложения. При загрузке стороннего кода внутри iframe, код получает новый контекст просмотра, и его источником является источник iframe. Следовательно, вызов sharedStorage.set() из iframe сохраняет данные в Shared Storage источника iframe.

Контекст первой стороны

Если на странице, созданной собственными средствами, встроен сторонний JavaScript-код, вызывающий методы sharedStorage.set() или sharedStorage.delete() , то пара ключ-значение сохраняется в контексте собственных средств.

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

Контекст третьей стороны

Пары ключ-значение можно хранить в контексте рекламных технологий или стороннего сервиса, создав iframe и вызвав методы set() или delete() в коде JavaScript внутри iframe.

Данные хранятся в контексте рекламных технологий или сторонних организаций.
На диаграмме изображены данные, хранящиеся в контексте рекламных технологий или сторонних организаций.

API частной агрегации

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

Для создания отчета вызовите функцию contributeToHistogram() внутри рабочего модуля, указав сегмент и значение. Сегмент представлен 128-битным беззнаковым целым числом, которое необходимо передать в функцию как BigInt . Значение — положительное целое число.

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

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

privateAggregation.contributeToHistogram({
  bucket: BigInt(myBucket),
  value: parseInt(myBucketValue)
});

Реализация операций с общим хранилищем и частной агрегацией

По умолчанию при использовании общего хранилища с createWorklet() источником данных для раздела будет являться источник контекста просмотра , вызывающего функцию, а не источник самого скрипта рабочего блока.

Чтобы изменить поведение по умолчанию, установите свойство dataOrigin при вызове createWorklet .

  • dataOrigin: "context-origin" : (По умолчанию) Данные хранятся в общем хранилище источника контекста просмотра, вызвавшего запрос.
  • dataOrigin: "script-origin" : Данные хранятся в общем хранилище источника скрипта рабочего модуля. Для включения этого режима требуется подтверждение согласия.
  • dataOrigin: "https://custom-data-origin.example" : Данные хранятся в общем хранилище пользовательского источника данных. Для включения этого режима требуется согласие владельца пользовательского источника данных, как подробно описано в разделе "Пользовательский источник данных" .
sharedStorage.createWorklet(scriptUrl, {dataOrigin: "script-origin"});

Для включения этой функции при использовании "script-origin" или пользовательского источника, конечная точка скрипта должна отвечать заголовком Shared-Storage-Cross-Origin-Worklet-Allowed . Для междоменных запросов также необходимо включить CORS .

Shared-Storage-Cross-Origin-Worklet-Allowed : ?1
Access-Control-Allow-Origin: *

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

Использование iframe с другим источником

Для вызова функции общего хранилища необходим iframe.

В iframe объявления загрузите модуль worklet, вызвав addModule() . Чтобы запустить метод, зарегистрированный в файле worklet sharedStorageWorklet.js , в том же JavaScript-коде iframe объявления вызовите метод sharedStorage.run() .

const sharedStorageWorklet = await window.sharedStorage.createWorklet(
  'https://any-origin.example/modules/sharedStorageWorklet.js'
);
await sharedStorageWorklet.run('shared-storage-report', {
  data: { campaignId: '1234' },
});

В скрипте рабочего блока вам потребуется создать класс с асинхронным методом run и register его для запуска во фрейме рекламы. Внутри sharedStorageWorklet.js :

class SharedStorageReportOperation {
  async run(data) {
    // Other code goes here.
    bucket = getBucket(...);
    value = getValue(...);
    privateAggregation.contributeToHistogram({
      bucket,
      value
    });
  }
}
register('shared-storage-report', SharedStorageReportOperation);

Использование междоменного запроса

Функция Shared Storage and Private Aggregation позволяет создавать рабочие модули из разных источников без необходимости использования iframe-элементов, поддерживающих разные источники.

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

async function crossOriginCall() {
  const privateAggregationWorklet = await sharedStorage.createWorklet(
    'https://cross-origin.example/js/worklet.js',
    { dataOrigin: 'script-origin' }
  );
  await privateAggregationWorklet.run('pa-worklet');
}
crossOriginCall();

Кросс-доменный JavaScript-эндпоинт должен ответить заголовками Shared-Storage-Cross-Origin-Worklet-Allowed и указать, что для запроса включена поддержка CORS.

Shared-Storage-Cross-Origin-Worklet-Allowed : ?1

В рабочих процессах, созданных с помощью createWorklet() будут присутствовать selectURL и run() . addModule() для этого недоступен.

class CrossOriginWorklet {
  async run(data){
    // Other code goes here.
    bucket = getBucket(...);
    value = getValue(...);
    privateAggregation.contributeToHistogram({
      bucket,
      value
    });
  }
}

Источник пользовательских данных

Если dataOrigin указан на допустимый источник, владелец dataOrigin должен дать согласие на обработку данных в Shared Storage для этого dataOrigin , разместив JSON-файл со списком источников скриптов рабочих модулей по пути /.well-known/shared-storage/trusted-origins . Файл должен представлять собой массив объектов с ключами scriptOrigin и contextOrigin . Значения для этих ключей могут быть либо строкой, либо массивом строк.

Создайте файл trusted-origins , используя следующую информацию:

  • Контекст вызывающего абонента
  • Источник и URL скрипта рабочего модуля
  • Источник и владелец данных

В следующей таблице показано, как можно сформировать файл trusted-origins на основе этой информации:

Контекст вызывающего абонента URL скрипта рабочего модуля Источник данных Владелец данных JSON-файл доверенных источников владельца источника данных
https://publisher.example https://publisher.example/script.js контекст-источник https://publisher.example JSON не требуется
https://publisher.example https://ad.example/script.js script-origin https://ad.example JSON не требуется
https://publisher.example https://cdn-ad.example/script.js https://ad.example https://ad.example
[{
  "scriptOrigin": "https://cdn-ad.example",
  "contextOrigin": "https://publisher.example"
}]
      
Любой звонящий https://cdn-ad.example/script.js https://ad.example https://ad.example
[{
  "scriptOrigin": "https://cdn-ad.example",
  "contextOrigin": "*"
}]
      
https://publisher-a.example, ИЛИ https://publisher-b.example https://cdn-ad.example/script.js https://ad.example https://ad.example
[{
  "scriptOrigin": "https://cdn-ad.example",
  "contextOrigin": [
      "https://publisher-a.example",
      "https://publisher-b.example"
  ]
}]
      
https://publisher.example https://cdn-a-ad.example/script.js, ИЛИ https://cdn-b-ad.example/script.js https://ad.example https://ad.example
[{
  "scriptOrigin": [
    "https://cdn-a-ad.example",
    "https://cdn-b-ad.example"
  ],
  "contextOrigin": "https://publisher.example"
}]
      

Например, следующий JSON-файл может быть размещен по адресу https://custom-data-origin.example/.well-known/shared-storage/trusted-origins и объединить все разрешенные обработчики данных из Shared Storage для источника https://custom-data-origin.example .

[
  {
    "scriptOrigin": "https://script-origin.a.example",
    "contextOrigin": "https://context-origin.a.example"
  },
  {
    "scriptOrigin": "https://script-origin.b.example",
    "contextOrigin": [
      "https://context-origin.a.example",
      "https://context-origin.b.example"
    ]
}]

Следующие шаги

На следующих страницах описаны важные аспекты API для общего хранилища и частной агрегации.

После ознакомления с API вы можете начать сбор отчетов, которые отправляются в виде POST-запроса на следующие конечные точки в формате JSON в теле запроса.

  • Отладочные отчеты - context-origin/.well-known/private-aggregation/debug/report-shared-storage
  • Отчеты - context-origin/.well-known/private-aggregation/report-shared-storage

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

Поделитесь своим мнением.

Вы можете оставить свой отзыв об API и документации на GitHub.