儲存空間存取權 API

瀏覽器封鎖第三方 Cookie、使用者設定和儲存空間分割,對依賴 Cookie 和其他嵌入式環境儲存空間的網站和服務 (例如驗證等使用者歷程) 構成挑戰。Storage Access API (SAA) 可讓這些用途繼續運作,同時盡可能限制跨網站追蹤。

導入狀態

Browser Support

  • Chrome: 119.
  • Edge: 85.
  • Firefox: 65.
  • Safari: 11.1.

Source

Storage Access API 適用於所有主要瀏覽器,但瀏覽器之間實作方式略有差異。本文相關章節已醒目顯示這些差異。

我們將持續解決所有尚未解決的阻礙問題,然後將 API 標準化

什麼是 Storage Access API?

Storage Access API 是 JavaScript API,可讓 iframe 在瀏覽器設定會拒絕存取儲存空間的情況下,要求儲存空間存取權。如果用途需要載入跨網站資源,嵌入內容可以使用 API 視需要向使用者要求存取權。

如果儲存空間要求獲得授權,iframe 就能存取未分割的 Cookie 和儲存空間,使用者以頂層網站的形式造訪時,也能存取這些資料。

Storage Access API 可提供特定的未分割 Cookie 和儲存空間存取權,盡量減少對使用者的負擔,同時防止一般未分割 Cookie 和儲存空間存取權 (這類存取權通常用於追蹤使用者)。

用途

部分第三方嵌入內容需要存取未分割的 Cookie 或儲存空間,才能提供更優質的使用者體驗。如果限制第三方 Cookie 並啟用儲存空間分割功能,就無法存取這些項目。

用途包括:

  • 需要登入工作階段詳細資料的內嵌留言小工具。
  • 需要登入工作階段詳細資料的社群媒體「喜歡」按鈕。
  • 需要登入工作階段詳細資料的內嵌文件。
  • 為影片嵌入提供進階體驗 (例如不向登入使用者顯示廣告,或瞭解使用者對隱藏式輔助字幕的偏好設定,或限制特定影片類型)。
  • 內建付款系統。

許多用途都涉及在嵌入的 iframe 中保留登入存取權。

何時應使用 Storage Access API 而非其他 API

Storage Access API 是未分割 Cookie 和儲存空間的替代方案之一,因此請務必瞭解何時應使用這個 API,而非其他 API。這項功能適用於下列用途:

  • 使用者會與嵌入的內容互動,也就是說,這不是被動式或隱藏式 iframe。
  • 使用者在頂層環境中造訪嵌入式來源,也就是該來源未嵌入其他網站。

您可以使用其他 API 處理各種用途:

  • 具有獨立分區狀態的 Cookie (CHIPS) 可讓開發人員選擇將 Cookie 儲存在「分區」儲存空間,每個頂層網站都有獨立的 Cookie Jar。舉例來說,第三方網路即時通訊小工具可能會設定 Cookie 來儲存工作階段資訊。系統會為每個網站儲存工作階段資訊,因此在嵌入小工具的其他網站上,不必存取小工具設定的 Cookie。如果嵌入的第三方小工具需要跨不同來源共用相同資訊 (例如登入的工作階段詳細資料或偏好設定),Storage Access API 就很有用。
  • 儲存空間分割可讓跨網站 iframe 使用現有的 JavaScript 儲存機制,同時依網站分割基礎儲存空間。這樣一來,其他網站上的相同嵌入內容就無法存取某個網站的嵌入式儲存空間。
  • 相關網站集合 (RWS) 可讓機構聲明網站間的關係,以便瀏覽器基於特定目的,允許有限的未分割 Cookie 和儲存空間存取權。網站仍須透過 Storage Access API 要求存取權,但對於集合中的網站,系統可直接授予存取權,不必提示使用者。
  • Federated Credential Management (FedCM) 是一種保護隱私權的聯合身分識別服務方法。Storage Access API 負責處理登入後存取未分割的 Cookie 和儲存空間。在某些情況下,FedCM 可做為 Storage Access API 的替代解決方案,而且由於 FedCM 提供的瀏覽器提示更著重於登入,因此可能更適合使用。不過,採用 FedCM 通常需要對程式碼進行額外變更,例如支援其 HTTP 端點。
  • 此外,還有防詐欺廣告相關評估 API,Storage Access API 並非用來解決這些問題。

使用 Storage Access API

Storage Access API 有兩種以 Promise 為基礎的方法:

這項功能也與 Permissions API 整合。這項功能可讓您在第三方環境中檢查儲存空間存取權的狀態,判斷系統是否會自動授予 document.requestStorageAccess() 的呼叫權限:

使用 hasStorageAccess() 方法

網站首次載入時,可以使用 hasStorageAccess() 方法檢查是否已授予第三方 Cookie 的存取權。

// Set a hasAccess boolean variable which defaults to false.
let hasAccess = false;

async function handleCookieAccessInit() {
  if (!document.hasStorageAccess) {
    // Storage Access API is not supported so best we can do is
    // hope it's an older browser that doesn't block 3P cookies.
    hasAccess = true;
  } else {
    // Check whether access has been granted using the Storage Access API.
    hasAccess = await document.hasStorageAccess();
    if (!hasAccess) {
      // Handle the lack of access (covered later)
    }
  }
  if (hasAccess) {
    // Use the cookies.
  }
}
handleCookieAccessInit();

Storage Access API 只會在 iframe 文件呼叫 requestStorageAccess(), 後授予儲存空間存取權,因此 hasStorageAccess() 可能會傳回 false,例如使用者預設封鎖第三方 Cookie 時。 (不過,即使使用者預設封鎖第三方 Cookie,網站專屬的使用者設定也可能允許特定網站存取 Cookie)。 使用這個 API 取得的儲存空間存取權,會在 iframe 內的同源導覽中保留,特別是允許在授予存取權後重新載入網頁,因為這些網頁需要在 HTML 文件的初始要求中提供 Cookie。

使用 requestStorageAccess()

如果 iframe 沒有存取權,可能需要使用 requestStorageAccess() 方法要求存取權:

if (!hasAccess) {
  try {
    await document.requestStorageAccess();
  } catch (err) {
    // Access was not granted and it may be gated behind an interaction
    return;
  }
}

首次要求時,使用者可能需要透過瀏覽器提示核准這項存取權,之後 Promise 會解析,或因使用 await 而導致例外狀況。

為防濫用,只有在使用者互動後,系統才會顯示這個瀏覽器提示。因此,requestStorageAccess() 必須先從使用者啟動的事件處理常式呼叫,而不是在 iframe 載入時立即呼叫:

async function doClick() {

  // Only do this extra check if access hasn't already been given
  // based on the hasAccess variable.
  if (!hasAccess) {
    try {
      await document.requestStorageAccess();
      hasAccess = true; // Can assume this was true if requestStorageAccess() did not reject.
    } catch (err) {
      // Access was not granted.
      return;
    }
  }

  if (hasAccess) {
    // Use the cookies
  }
}

document.querySelector('#my-button').addEventListener('click', doClick);

權限提示

使用者第一次點選按鈕時,瀏覽器提示通常會自動顯示在網址列中。下方的螢幕截圖是 Chrome 的提示範例,但其他瀏覽器也有類似的 UI:

Chrome Storage Access API 權限提示。
Chrome 的 Storage Access API 權限提示

在某些情況下,瀏覽器可能會略過提示,並自動提供權限:

  • 如果頁面和 iframe 在接受提示後的 30 天內曾使用過。
  • 如果內嵌的 iframe 屬於相關網站集合
  • 如果 FedCM 用做儲存空間存取權的信任信號
  • 在 Firefox 中,系統也會略過前五次嘗試的提示,適用於已知網站 (您曾與頂層網站互動)。

或者,在某些情況下,系統可能會自動拒絕要求,而不顯示提示:

  • 如果使用者先前未曾造訪並與 iframe 所屬網站互動,且該網站並非以 iframe 形式呈現,而是以頂層文件形式呈現,也就是說,Storage Access API 僅適用於使用者先前在第一方環境中造訪過的嵌入式網站。
  • 如果使用者互動事件後未事先核准提示,就從使用者互動事件外部呼叫 requestStorageAccess() 方法。

使用者首次使用時會收到提示,但之後在 Chrome 和 Firefox 中造訪時,系統會自動解決 requestStorageAccess(),不會顯示提示,也不需要使用者互動。請注意,Safari 一律需要使用者互動。

由於系統可能會在沒有提示或使用者互動的情況下授予 Cookie 和儲存空間存取權,因此在支援這項功能的瀏覽器 (Chrome 和 Firefox) 中,您通常可以在網頁載入時呼叫 requestStorageAccess(),在使用者互動前取得未分割的 Cookie 或儲存空間存取權。這樣您就能立即存取未分割的 Cookie 和儲存空間,即使使用者尚未與 iframe 互動,也能提供更完整的使用體驗。在某些情況下,這比等待使用者互動更能提升使用者體驗。

將 FedCM 做為 SAA 的信任信號

FedCM (Federated Credential Management) 是一種可保護隱私權的聯合身分服務 (例如「使用...登入」),不依賴第三方 Cookie 或導覽重新導向。

當使用者登入依賴方 (RP) 時,如果該依賴方含有來自第三方身分識別提供者 (IdP) 的嵌入內容,且已啟用 FedCM,嵌入的 IdP 內容就能自動取得儲存空間存取權,存取自己的頂層未分割 Cookie。如要透過 FedCM 啟用自動儲存空間存取權,必須符合下列條件:

  • FedCM 驗證 (使用者登入狀態) 必須處於啟用狀態。
  • RP 已設定 identity-credentials-get 權限,例如:
<iframe src="https://idp.example" allow="identity-credentials-get"></iframe>

舉例來說,idp.example iframe 會嵌入 rp.example。使用者透過 FedCM 登入時,idp.example iframe 可以要求存取自己的頂層 Cookie。

rp.example 會發出 FedCM 呼叫,讓使用者透過身分識別提供者 idp.example 登入:

// The user will be asked to grant FedCM permission.
const cred = await navigator.credentials.get({
  identity: {
    providers: [{
      configURL: 'https://idp.example/fedcm.json',
      clientId: '123',
    }],
  },
});

使用者登入後,只要 RP 已透過權限政策明確允許,IdP 即可從 idp.example iframe 內呼叫 requestStorageAccess()。 系統會自動授予嵌入內容頂層 Cookie 的儲存空間存取權,不必啟用使用者或顯示其他權限提示

// Make this call within the embedded IdP iframe:

// No user gesture is needed, and the storage access will be auto-granted.
await document.requestStorageAccess();

// This returns `true`.
const hasAccess = await document.hasStorageAccess();

只要使用者透過 FedCM 登入,系統就會自動授予權限。驗證停用後,系統會套用標準 SAA 規定,授予儲存空間存取權。

如要要求存取未經分割的本機儲存空間,請將 types 參數傳遞至 requestStorageAccess 呼叫。舉例來說,如要要求存取未分割的本機儲存空間,可以呼叫 requestStorageAccess({localStorage: true})

如果啟用第三方 Cookie,這個方法會授予存取權,不需要使用者啟用或任何權限提示。如果使用者已停用第三方 Cookie,系統必須先提示使用者,才能授予儲存空間存取權。

Storage Access API 的流程圖,說明如何取得非 Cookie 儲存空間的存取權。
非 Cookie 儲存空間存取要求流程。

首先,請檢查瀏覽器是否已具備儲存空間存取權:

async function hasCookieAccess(){
  // Check if Storage Access API is supported
  if (!document.requestStorageAccess) {
    // Storage Access API is not supported, so we assume it's an older browser that doesn't partition storage.
    throw new Error("requestStorageAccess is not supported")
  }

  // Check if access has already been granted or if the user has 3-party cookies enabled
  return document.hasStorageAccess();
}

如果已啟用第三方 Cookie,請要求儲存空間存取權:

// Request storage access and return the storage handle
async function requestStorageHandle(){
// You can request for multiple types of non-cookie storage
// at once, or request for all of them with all:true
return document.requestStorageAccess({all:true});
}

如果第三方 Cookie 遭到封鎖 (例如在無痕模式下),請檢查查詢權限,決定是否需要使用者提示。navigator.permissions.query({name: 'storage-access'}) 權限狀態可能包含下列值:

  • granted。使用者已授予存取權。呼叫 requestStorageAccess 即可取得未分割的儲存空間存取權,不必額外提示使用者。
  • prompt。使用者尚未授予存取權。設定點擊監聽器,並在使用者互動後再次呼叫 requestStorageAccess
  • error。系統不支援這項權限。如果支援 Storage Access API,系統可能也會支援這項權限。
// Returns `granted`, or `prompt`; or throws an error if storage-access
// permission is not supported
async function getStorageAccessPermission(){
  // Check the storage-access permission
  // Wrap this in a try/catch for browsers that support the
  // Storage Access API but not this permission check
    return navigator.permissions.query({name: 'storage-access'});
}

如要完整處理非 Cookie 分區儲存空間,可以按照下列流程實作:

async function getStorageHandle() {
    // Check if the user has 3-party cookie access
    if (await hasCookieAccess()) {
    // If the user has access, requestStorageAccess() will resolve automatically
        return requestStorageHandle();
    }

    // If the browser blocks third party cookies, check if the user has
    // accepted the prompt and granted access. If they have,
    // requestStorageAccess() will resolve automatically
    const permission = await getStorageAccessPermission();
    if (permission == 'granted') { // User has seen prompt and granted access
        return requestStorageHandle();
    }

    // Wait for user activation to prompt the user again
    // (or put your silent failure logic here instead)
    return new Promise((resolve, reject) => {
        document.querySelector('#myButton').addEventListener(e => {
            requestStorageHandle().then(resolve, reject);
        })
    })
}

// Use your storage
getStorageHandle().then(handle=>{
    handle.indexedDB.open(...);
}).catch(() => {
    // If the promise is rejected, you can use regular partitioned storage
    indexedDB.open(...);
})

使用儲存空間存取權標頭後續載入

建議使用儲存空間存取權標頭,以更有效率的方式載入內嵌內容,包括非 iframe 資源。這項功能適用於 Chrome 133 以上版本。有了儲存空間存取權標頭,瀏覽器就能辨識使用者是否已在目前環境中,將 storage-access 權限授予第三方來源,並在後續造訪期間載入可存取未分割 Cookie 的資源。

儲存空間存取標頭流程

使用儲存空間存取權標頭時,後續網頁造訪會觸發下列流程:

  1. 使用者先前曾造訪內嵌 calendar.example 資源的 website.example,並透過 document.requestStorageAccess() 呼叫授予 storage-access 權限
  2. 使用者再次造訪內嵌 calendar.example 資源的 website.example這項要求仍無法存取 Cookie,與先前相同。 不過,使用者先前已授予 storage-access 權限,且擷取作業包含 Sec-Fetch-Storage-Access: inactive 標頭,表示未經分割的 Cookie 存取權可用但未啟用。
  3. calendar.example 伺服器會以 Activate-Storage-Access: retry; allowed-origin='<origin>' 標頭 (在本例中,<origin> 會是 https://website.example) 回應,指出資源擷取作業需要使用具有 storage-access 權限的未分割 Cookie。
  4. 瀏覽器會重試要求,這次會加入未經分割的 Cookie (為這項擷取作業和後續擷取作業啟用 storage-access 權限)。
  5. calendar.example 伺服器會傳回個人化 iframe 內容。回應會包含 Activate-Storage-Access: load 標頭,表示瀏覽器應載入已啟用 storage-access 權限的內容 (換句話說,載入時應具備未經區隔的 Cookie 存取權,如同已呼叫 document.requestStorageAccess())。
  6. 使用者代理程式會使用 storage-access 權限載入 iframe 內容,且不會分割 Cookie 存取權。完成這個步驟後,小工具就能正常運作。
流程圖:說明儲存空間存取標頭流程。
儲存空間存取標頭流程圖。

使用儲存空間存取權標頭

下表列出儲存空間存取權標頭。

Flow 標頭 說明
要求 Sec-Fetch-Storage-Access
注意:在包含憑證的跨網站要求中 (例如 new Request('request.example', { credentials: 'include' });),瀏覽器會自動傳送這個標頭。
none 嵌入內容沒有儲存空間存取權。
inactive 嵌入內容有權限,但未使用。
要求也必須包含 Origin 標頭。
active 嵌入內容可存取未分區的 Cookie。
回應 Activate-Storage-Access load 指示瀏覽器授予嵌入者所要求資源的未分割 Cookie 存取權。
如果已授予 storage-access 權限,加入這個標頭就等同於呼叫document.requestStorageAccess()。也就是說,系統不會向使用者顯示其他提示。
retry 指示瀏覽器啟用儲存空間存取權限,然後重試要求。
allowed-origin <origin> 指定允許啟動憑證要求 (例如 https://site.example*)。

舉例來說,您可以使用 Storage Access Headers 從第三方載入圖片:

  // On the client side
  <img src="https://server.example/image">

在這種情況下,server.example 應在伺服器端實作下列邏輯:

  app.get('/image', (req, res) => {
  const storageAccessHeader = req.headers['sec-fetch-storage-access'];

  if (storageAccessHeader === 'inactive') {
    // The user needs to grant permission, trigger a prompt

    // Check if the requesting origin is allowed
    // to send credentialed requests to this server.
    // Assuming the `validate_origin(origin)` method is previously defined:
    if (!validate_origin(req.headers.origin)) {
      res.status(401).send(req.headers.origin +
        ' is not allowed to send credentialed requests to this server.');
      return;
    }
    // 'retry' header value indicates that the content load request should be re-sent after the user has granted permissions
    res.set('Activate-Storage-Access', `retry; allowed-origin='${req.headers.origin}'`);
    res.status(401).send('This resource requires storage access. Please grant permission.');
  } else if (storageAccessHeader === 'active') {
    // User has granted permission, proceed with access
    res.set('Activate-Storage-Access', 'load');
    // Include the actual iframe content here
    res.send('This is the content that requires cookie access.');
  } else {
    // Handle other cases (e.g., 'Sec-Fetch-Storage-Access': 'none')
  }
});

儲存空間存取 API 示範會使用儲存空間存取標頭,嵌入第三方內容 (包括非 iframe 圖片)。

使用 storage-access 權限查詢

如要檢查是否可授予存取權,而不必與使用者互動,您可以檢查 storage-access 權限的狀態,並在不需要使用者動作時提早呼叫 requestStoreAccess(),而不是在需要互動時呼叫該權限,導致呼叫失敗。

此外,您也可以顯示不同內容 (例如登入按鈕),預先處理提示需求。

下列程式碼會在先前的範例中新增 storage-access 權限檢查:

// Set a hasAccess boolean variable which defaults to false except for
// browsers which don't support the API - where we assume
// such browsers also don't block third-party cookies.
let hasAccess = false;

async function hasCookieAccess() {
  // Check if Storage Access API is supported
  if (!document.requestStorageAccess) {
    // Storage Access API is not supported so best we can do is
    // hope it's an older browser that doesn't block 3P cookies.
    return true;
  }

  // Check if access has already been granted
  if (await document.hasStorageAccess()) {
    return true;
  }

  // Check the storage-access permission
  // Wrap this in a try/catch for browsers that support the
  // Storage Access API but not this permission check
  // (e.g. Safari and earlier versions of Firefox).
  let permission;
  try {
    permission = await navigator.permissions.query(
      {name: 'storage-access'}
    );
  } catch (error) {
    // storage-access permission not supported. Assume no cookie access.
    return false;
  }

    if (permission) {
    if (permission.state === 'granted') {
      // Permission has previously been granted so can just call
      // requestStorageAccess() without a user interaction and
      // it will resolve automatically.
      try {
        await document.requestStorageAccess();
        return true;
      } catch (error) {
        // This shouldn't really fail if access is granted, but return false
        // if it does.
        return false;
      }
    } else if (permission.state === 'prompt') {
      // Need to call requestStorageAccess() after a user interaction
      // (potentially with a prompt). Can't do anything further here,
      // so handle this in the click handler.
      return false;
          } else if (permission.state === 'denied') {
            // Not used: see https://github.com/privacycg/storage-access/issues/149
      return false;
          }
    }

  // By default return false, though should really be caught by earlier tests.
  return false;
}

async function handleCookieAccessInit() {
  hasAccess = await hasCookieAccess();

  if (hasAccess) {
    // Use the cookies.
  }
}

handleCookieAccessInit();

採用沙箱機制的 iframe

沙箱化 iframe 中使用 Storage Access API 時,必須具備下列沙箱權限:

  • 必須允許存取 Storage Access API。allow-storage-access-by-user-activation
  • 如要允許使用 JavaScript 呼叫 API,必須提供 allow-scripts
  • allow-same-origin 必須允許存取同源 Cookie 和其他儲存空間。

例如:

<iframe sandbox="allow-storage-access-by-user-activation
                 allow-scripts
                 allow-same-origin"
        src="..."></iframe>

如要在 Chrome 中透過 Storage Access API 存取跨網站 Cookie,必須使用下列兩個屬性設定 Cookie:

  • SameSite=None - 這是將 Cookie 標示為跨網站的必要條件
  • Secure - 確保只有 HTTPS 網站設定的 Cookie 可以存取。

在 Firefox 和 Safari 中,Cookie 預設為 SameSite=None,且不會將 SAA 限制為 Secure Cookie,因此不需要這些屬性。建議您明確指定 SameSite 屬性,並一律使用 Secure Cookie。

頂層頁面存取權

Storage Access API 的用途是在內嵌 iframe 中啟用第三方 Cookie 存取權。

如果頂層網頁需要存取第三方 Cookie,也有其他使用案例。舉例來說,網站擁有者可能想直接在頂層文件中加入受 Cookie 限制的圖片或指令碼,而不是在 iframe 中加入。為解決這個用途,Chrome 提議擴充 Storage Access API,新增 requestStorageAccessFor() 方法。

requestStorageAccessFor() 方法

Browser Support

  • Chrome: 119.
  • Edge: 119.
  • Firefox: not supported.
  • Safari: not supported.

Source

requestStorageAccessFor() 方法的運作方式與 requestStorageAccess() 類似,但適用於頂層資源。這項功能只能用於相關網站集合中的網站,避免授予第三方 Cookie 一般存取權。

如要進一步瞭解如何使用 requestStorageAccessFor(),請參閱相關網站集合:開發人員指南

top-level-storage-access 權限查詢

Browser Support

  • Chrome: 113.
  • Edge: 113.
  • Firefox: not supported.
  • Safari: not supported.

storage-access 權限類似,您可以使用 top-level-storage-access 權限檢查是否可授予 requestStorageAccessFor() 的存取權。

搭配使用相關網站集合時,Storage Access API 有何不同?

搭配使用相關網站集合與 Storage Access API 時,可使用下列表格詳述的特定額外功能:

未啟用 RWS 支援 RWS
需要使用者手勢才能啟動儲存空間存取權要求
要求使用者先在頂層環境中造訪所要求的儲存空間來源,才能授予存取權
初次使用者的提示可以略過
如果先前已授予存取權,則requestStorageAccess不需呼叫
自動授予相關網站集合中其他網域的存取權
支援頂層頁面存取權的 requestStorageAccessFor
使用 Storage Access API 時,有無相關網站集合的差異

示範:設定及存取 Cookie

以下示範在示範的第一個畫面中,您自行設定的 Cookie 如何在示範的第二個網站中,於嵌入式框架中存取:

storage-access-api-demo.glitch.me

如要使用這個範例,瀏覽器必須停用第三方 Cookie:

  • Chrome 118 以上版本,且已設定 chrome://flags/#test-third-party-cookie-phaseout 旗標並重新啟動瀏覽器。
  • Firefox
  • Safari

示範:設定本機儲存空間

以下示範如何使用 Storage Access API,從第三方 iframe 存取未分割的 Broadcast Channel:

https://saa-beyond-cookies.glitch.me/

如要使用這個範例,必須啟用 test-third-party-cookie-phaseout 旗標,並使用 Chrome 125 以上版本。

資源