實作共用儲存空間和私密匯總的快速入門導覽課程

本文是使用 Shared Storage 和 Private Aggregation 的快速入門指南。您需要瞭解這兩個 API,因為 Shared Storage 會儲存值,而 Private Aggregation 則會建立可匯總的報表。

目標對象:廣告技術和評估供應商。

Shared Storage API

為防止跨網站追蹤,瀏覽器開始分割所有形式的儲存空間,包括本機儲存空間、Cookie 等。但有些用途需要未分割的儲存空間。Shared Storage API 提供不同頂層網站的無限寫入權限,同時保留隱私權保護的讀取權限。

Shared Storage 僅限於內容來源 (sharedStorage 的呼叫端)。

每個來源的共用儲存空間容量有限,且每筆項目都有字元數上限。如果達到上限,系統就不會再儲存任何輸入內容。資料儲存空間限制請參閱共用儲存空間說明

叫用 Shared Storage

廣告技術人員可以使用 JavaScript 或回應標頭,將資料寫入共用儲存空間。從共用儲存空間讀取資料時,只會在稱為「小程式」的獨立 JavaScript 環境中進行。

  • 使用 JavaScript:廣告技術可以執行特定的 Shared Storage 函式,例如在 JavaScript Worklet 以外設定、附加及刪除值。不過,讀取共用儲存空間和執行私人匯總等函式必須透過 JavaScript worklet 完成。如要瞭解可在 JavaScript Worklet 外部使用的方法,請參閱「建議的 API 介面 - Worklet 外部」。

    您可以在建議的 API 介面 - Worklet 中,找到作業期間在 Worklet 中使用的方法。

  • 使用回應標頭

    與 JavaScript 類似,只有在共用儲存空間中設定、附加及刪除值等特定函式,才能使用回應標頭。如要在回應標頭中使用共用儲存空間,要求標頭中必須包含 Shared-Storage-Writable: ?1

    如要從用戶端發出要求,請根據所選方法執行下列程式碼:

    • 使用fetch()

      fetch("https://a.example/path/for/updates", {sharedStorageWritable: true});
      
    • 使用 iframeimg 標記

      <iframe src="https://a.example/path/for/updates" sharedstoragewritable></iframe>
      
    • 使用含有 iframeimg 標記的 IDL 屬性

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

詳情請參閱「共用儲存空間:回應標頭」。

寫入共用儲存空間

如要寫入 Shared Storage,請從 JavaScript worklet 內或外部呼叫 sharedStorage.set()。如果從工作單外部呼叫,資料會寫入呼叫來源的瀏覽環境。如果從 Worklet 內部呼叫,資料會寫入載入 Worklet 的瀏覽環境來源。設定的金鑰會在上次更新後的 30 天到期。

ignoreIfPresent 欄位為選填。如果存在且設為 true,則現有鍵不會更新。即使金鑰未更新,金鑰到期日也會在 set() 呼叫後延長 30 天。

如果在同一個網頁載入期間,使用相同鍵多次存取共用儲存空間,系統會覆寫該鍵的值。如果鍵需要保留先前的值,建議使用 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'}
    

    同樣地,在 worklet 內:

    sharedStorage.set('myKey', 'myValue1', { ignoreIfPresent: true });
    
  • 使用回應標頭

    您也可以使用回應標頭寫入共用儲存空間。如要這麼做,請在回應標頭中使用 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
    

    多個項目可以逗號分隔,並可合併 setappenddeleteclear

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

附加值

您可以使用附加方法,將值附加至現有鍵。如果金鑰不存在,呼叫 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'}
    

    如要在 worklet 內附加內容:

    sharedStorage.append('myKey', 'myValue1');
    
  • 使用回應標頭

    與在共用儲存空間中設定值類似,您可以使用回應標頭中的 Shared-Storage-Write 傳遞鍵/值組合。

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

批次更新值

您可以從 JavaScript worklet 內或外部呼叫 sharedStorage.batchUpdate(),並傳遞方法排序陣列,指定所選作業。每個方法建構函式接受的參數,與對應的個別方法 (用於設定、附加、刪除及清除) 相同。

您可以從 JavaScript 呼叫 batchUpdate(),或使用回應標頭:

  • 使用 JavaScript

    可與 batchUpdate() 搭配使用的 JavaScript 方法包括:

    • SharedStorageSetMethod():將鍵/值組合寫入共用儲存空間。
    • SharedStorageAppendMethod():將值附加至共用儲存空間中的現有鍵,或在鍵尚不存在時寫入鍵/值配對。
    • SharedStorageDeleteMethod():從共用儲存空間刪除鍵/值組合。
    • SharedStorageClearMethod():清除 Shared Storage 中的所有金鑰。
    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');
    

    如要從工作程式內刪除共用儲存空間中的資料,請按照下列步驟操作:

    sharedStorage.delete('myKey');
    

    如要從工作站外部一次刪除所有鍵:

    window.sharedStorage.clear();
    

    如要從工作單元中一次刪除所有金鑰:

    sharedStorage.clear();
    
  • 使用回應標頭

    如要使用回應標頭刪除值,您也可以在回應標頭中使用 Shared-Storage-Write 傳遞要刪除的鍵。

    delete;key="myKey"
    

    如要使用回應標頭刪除所有金鑰,請按照下列步驟操作:

    clear;
    

從 Shared Storage 讀取 Protected Audience 興趣群組

您可以從共用儲存空間工作單讀取 Protected Audience 的興趣群組。interestGroups() 方法會傳回 StorageInterestGroup 物件陣列,包括 AuctionInterestGroupGenerateBidInterestGroup 屬性。

以下範例說明如何讀取瀏覽環境興趣群組,以及對擷取的興趣群組執行的一些可能作業。可能使用的兩種作業是找出興趣群組數量,以及找出出價次數最高的興趣群組。

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);
}

根據預設,系統會根據載入 Worklet 模組的瀏覽環境來源,判斷要讀取的興趣群組來源。如要進一步瞭解預設的 Worklet 來源和變更方式,請參閱「Shared Storage API Walkthrough」(Shared Storage API 逐步導覽) 中的「Executing Shared Storage and Private Aggregation」(執行 Shared Storage 和 Private Aggregation) 一節

選項

所有 Shared Storage 修飾符方法都支援選用選項物件做為最後一個引數。

withLock

withLock 選項為選用項目。如有指定,這個選項會指示方法在繼續操作前,使用 Web Locks API 取得所定義資源的鎖定。要求鎖定時,系統會傳遞鎖定名稱。這個名稱代表資源,其用量會在來源內的多個分頁、工作站或程式碼之間協調。

withLock 選項可與下列 Shared Storage 修飾符方法搭配使用:

  • set
  • 附加
  • 刪除
  • 關閉
  • 批次更新

您可以使用 JavaScript 或回應標頭設定鎖定:

  • 使用 JavaScript

    sharedStorage.set('myKey', 'myValue', { withLock: 'myResource' });
    
  • 使用回應標頭

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

共用儲存空間鎖定會依資料來源劃分。無論鎖定是在 windowworker 環境中,這些鎖定都與使用 LockManager request() 方法取得的鎖定無關。不過,這些鎖定與在 SharedStorageWorklet 環境中使用 request() 取得的鎖定具有相同範圍。

雖然 request() 方法提供各種設定選項,但在 Shared Storage 中取得的鎖定一律會遵守下列預設設定:

  • mode: "exclusive":不得同時持有其他同名鎖定。
  • steal: false:系統不會釋出同名的現有鎖定,以配合其他要求。
  • ifAvailable: false:要求會無限期等待,直到鎖定可用為止。
使用 withLock 的時機

如果有多個工作單同時執行 (例如網頁上有許多工作單,或不同分頁中有許多工作單),且每個工作單都查看相同資料,鎖定功能就非常實用。在這種情況下,建議使用鎖定功能包裝相關的 worklet 程式碼,確保一次只處理一份報表。

如果工作單中有多個需要一起讀取的鍵,且這些鍵的狀態應同步處理,鎖定功能也很有用。在這種情況下,應使用鎖定包裝 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() 可確保與其他同時取得相同鎖定的作業互斥。您只能對整個批次套用 batchUpdate()withLock 選項。如果將 withLock 套用至批次中的任何個別方法物件,系統會擲回例外狀況。

這個範例使用鎖定功能,確保工作單內的讀取和刪除作業會同時發生,避免工作單外的干擾。

下列 modify-multiple-keys.js 範例會使用 modify-lockkeyOnekeyTwo 設定新值,然後從工作單執行 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() 時,資料會寫入嵌入者的共用儲存空間。在 iframe 中載入第三方程式碼時,該程式碼會收到新的瀏覽環境,且來源為 iframe 的來源。因此,iframe 發出的 sharedStorage.set() 呼叫會將資料儲存至 iframe 來源的共用儲存空間。

第一方情境

如果第一方網頁內嵌的第三方 JavaScript 程式碼會呼叫 sharedStorage.set()sharedStorage.delete(),系統就會將鍵/值組合儲存在第一方環境中。

儲存在內嵌第三方 JavaScript 的第一方網頁中的資料。
這張圖顯示儲存在第一方網頁中的資料,以及嵌入的第三方 JavaScript。

第三方情境

建立 iframe 並從 iframe 內的 JavaScript 程式碼呼叫 set()delete(),即可將鍵/值組合儲存在廣告技術或第三方環境中。

儲存在廣告技術或第三方環境中的資料。
這張圖表說明儲存在廣告技術或第三方環境中的資料。

Private Aggregation API

如要評估儲存在 Shared Storage 中的可匯總資料,可以使用 Private Aggregation API。

如要建立報表,請在工作單中呼叫 contributeToHistogram(),並提供 bucket 和 value。儲存區會以不帶正負號的 128 位元整數表示,且必須以 BigInt 形式傳遞至函式。值為正整數。

為保護隱私權,報表的酬載 (包含 bucket 和值) 在傳輸過程中會經過加密,且只能使用匯總服務解密及匯總。

瀏覽器也會限制網站對輸出查詢的貢獻。具體來說,貢獻預算會限制特定瀏覽器在特定時間範圍內,從單一網站傳送至所有 bucket 的報表總數。如果超出目前預算,系統就不會產生報表。

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

執行 Shared Storage 和 Private Aggregation

根據預設,使用 createWorklet() 共用儲存空間時,資料分割區來源會是叫用瀏覽環境的來源,而不是 Worklet 指令碼本身的來源。

如要變更預設行為,請在呼叫 createWorklet 時設定 dataOrigin 屬性。

  • 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 執行跨來源指令碼,在這種情況下,共用儲存空間動作會位於第三方瀏覽環境中。

使用跨來源 iframe

您需要使用 iframe 叫用共用儲存空間小程式。

在廣告的 iframe 中,呼叫 addModule() 載入 worklet 模組。如要執行在 sharedStorageWorklet.js worklet 檔案中註冊的方法,請在相同的廣告 iframe JavaScript 中呼叫 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 該類別,以便在廣告的 iframe 中執行。內含: 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 和 Private Aggregation 可建立跨來源工作單元,不必使用跨來源 iframe。

第一方網頁也可以呼叫 createWorklet(),連線至跨來源 JavaScript 端點。建立 Worklet 時,您需要將 Worklet 的資料分割區來源設為指令碼來源。

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() 建立的 Worklet 會包含 selectURLrun()。 這項功能不支援「addModule()」。

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

自訂資料來源

如果 dataOrigin 設為有效來源,dataOrigin 的擁有者必須同意處理該 dataOrigin 的共用儲存空間,方法是在路徑 /.well-known/shared-storage/trusted-origins 託管列出 Worklet 指令碼來源的 JSON 檔案。檔案應為物件陣列,並包含 scriptOrigincontextOrigin 鍵。這些鍵的值可以是字串或字串陣列。

使用下列資訊建構 trusted-origins 檔案:

  • 來電者背景資訊
  • Worklet 指令碼來源和網址
  • 資料來源和擁有者

下表說明如何根據這項資訊建構 trusted-origins 檔案:

來電者背景資訊 Worklet 指令碼網址 資料來源 資料擁有者 資料來源擁有者的受信任來源 JSON 檔案
https://publisher.example https://publisher.example/script.js context-origin 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,並合併 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"
    ]
}]

後續步驟

以下頁面說明 Shared Storage API 和 Private Aggregation API 的重要面向。

熟悉 API 後,您就可以開始收集報表,這些報表會以 POST 要求的形式,透過要求主體中的 JSON 傳送至下列端點。

  • 偵錯報表 - context-origin/.well-known/private-aggregation/debug/report-shared-storage
  • 報表 - context-origin/.well-known/private-aggregation/report-shared-storage

收集報表後,您可以使用本機測試工具進行測試,或設定匯總服務的信任執行環境,取得匯總報表。

提供意見

您可以在 GitHub 上分享對 API 和說明文件的意見。