使用 Reporting API 監控網頁應用程式

使用 Reporting API 監控安全違規、已淘汰的 API 呼叫等。

Maud Nalpas
Maud Nalpas

有些錯誤只會在實際工作環境中發生。在本機或開發期間不會看到更新,因為「實際使用者」、「實際網路」和「實際裝置」會改變遊戲。Reporting API 可協助您找出部分這類錯誤,例如網站上的安全性違規或已淘汰及即將淘汰的 API 呼叫,並將這些錯誤傳送至您指定的端點。

您可以透過 HTTP 標頭宣告要監控的項目,且由瀏覽器操作。

設定 Reporting API 可讓您安心地解決當使用者遇到這類錯誤時,並可以據此進行修正。

本文將說明這個 API 的功能和使用方式。我們馬上來查查看!

示範和程式碼

Chrome 96 以上版本 (2021 年 10 月起的 Chrome Beta 版或 Canary 版) 開始,您就能看到 Reporting API 的實際運作情形。

總覽

圖表總結下列步驟,從產生報表到開發人員存取報表
報表的產生和傳送方式。

假設您的網站 (site.example) 含有內容安全性政策和文件政策。不清楚這些步驟嗎?沒關係,您還是可以瞭解這個範例。

您決定監控網站,以便瞭解何時違反這些政策,同時也想留意您的程式碼集可能使用的已淘汰或即將淘汰的 API。

如要這樣做,您需要設定 Reporting-Endpoints 標頭,並視需要在政策中透過 report-to 指令對應這些端點名稱。

Reporting-Endpoints: main-endpoint="https://reports.example/main", default="https://reports.example/default"
# Content-Security-Policy violations and Document-Policy violations
# will be sent to main-endpoint
Content-Security-Policy: script-src 'self'; object-src 'none'; report-to main-endpoint;
Document-Policy: document-write=?0; report-to=main-endpoint;
# Deprecation reports don't need an explicit endpoint because
# these reports are always sent to the `default` endpoint

發生無法預期的情況,導致部分使用者違反這些政策。

違規示例

index.html

<script src="script.js"></script>
<!-- CSP VIOLATION: Try to load a script that's forbidden as per the Content-Security-Policy -->
<script src="https://example.com/script.js"></script>

script.js,由 index.html 載入

// DOCUMENT-POLICY VIOLATION: Attempt to use document.write despite the document policy
try {
  document.write('<h1>hi</h1>');
} catch (e) {
  console.log(e);
}
// DEPRECATION: Call a deprecated API
const webkitStorageInfo = window.webkitStorageInfo;

瀏覽器會產生 CSP 違規報告、文件政策違規報告,以及擷取這些問題的淘汰報告。

在一段短暫的延遲時間內 (最多一分鐘),瀏覽器會將報表傳送到針對此違規類型設定的端點。報表是由瀏覽器本身 (而非您的網站或您的網站) 傳送「架構外」

端點會接收這些報表。

您現在可以存取這些端點的報表,並監控發生錯誤的情況。您可以開始排解使用者遇到的問題。

範例報表

{
  "age": 2,
  "body": {
    "blockedURL": "https://site2.example/script.js",
    "disposition": "enforce",
    "documentURL": "https://site.example",
    "effectiveDirective": "script-src-elem",
    "originalPolicy": "script-src 'self'; object-src 'none'; report-to main-endpoint;",
    "referrer": "https://site.example",
    "sample": "",
    "statusCode": 200
  },
  "type": "csp-violation",
  "url": "https://site.example",
  "user_agent": "Mozilla/5.0... Chrome/92.0.4504.0"
}

用途和報表類型

您可以設定 Reporting API,協助您監控整個網站中發生的多種有趣的警告或問題:

報告類型 產生報表的情況示例
CSP 違規 (僅限第 3 級) 您已在某個網頁上設定 Content-Security-Policy (CSP),但該網頁嘗試載入 CSP 不允許的指令碼。
違反 COOP 您在頁面上設定了 Cross-Origin-Opener-Policy,但跨來源視窗嘗試直接與文件互動。
違反 COEP 您已在頁面上設定 Cross-Origin-Embedder-Policy,但文件包含跨來源 iframe,且未選擇由跨來源文件載入。
違反文件政策 網頁有文件政策禁止使用 document.write,但指令碼嘗試呼叫 document.write
違反權限政策 網頁設有禁止使用麥克風的權限政策,以及要求音訊輸入的腳本。
淘汰警告 頁面使用已淘汰或即將淘汰的 API,直接呼叫該 API 或透過頂層第三方指令碼呼叫。
干預 網頁嘗試執行的動作,因安全性、效能或使用者體驗因素而遭瀏覽器拒絕。Chrome 中的範例:網頁在慢速網路上使用 document.write,或是在使用者尚未互動的跨來源框架中呼叫 navigator.vibrate
當機 開啟網站時,瀏覽器會當機。

報表

報表長什麼樣子?

瀏覽器會將報表傳送至您設定的端點。它會傳送以下要求:

POST
Content-Type: application/reports+json

這些要求的酬載是報表清單。

報表清單範例

[
  {
    "age": 420,
    "body": {
      "columnNumber": 12,
      "disposition": "enforce",
      "lineNumber": 11,
      "message": "Document policy violation: document-write is not allowed in this document.",
      "policyId": "document-write",
      "sourceFile": "https://site.example/script.js"
    },
    "type": "document-policy-violation",
    "url": "https://site.example/",
    "user_agent": "Mozilla/5.0... Chrome/92.0.4504.0"
  },
  {
    "age": 510,
    "body": {
      "blockedURL": "https://site.example/img.jpg",
      "destination": "image",
      "disposition": "enforce",
      "type": "corp"
    },
    "type": "coep",
    "url": "https://dummy.example/",
    "user_agent": "Mozilla/5.0... Chrome/92.0.4504.0"
  }
]

以下是各報表中的資料:

欄位 說明
age 報表時間戳記與目前時間之間的毫秒數。
body 實際報表資料,已序列化為 JSON 字串。報表「body」中包含的欄位取決於報表的type⚠️ 不同類型的報表具有不同的主體。 如要查看各報表類型的確切內容,請參閱報表示範端點 ,並按照操作說明產生範例報表。
type 報表類型,例如 csp-violationcoep
url 產生報表的文件或工作者地址。從這個網址移除機密資料,例如使用者名稱、密碼和片段。
user_agent 報表產生要求的 User-Agent 標頭。

憑證報表

報表端點的來源與產生報表的網頁相同,因此會在包含報表的要求中接收憑證 (Cookie)。

憑證可能會提供報表的其他實用背景資訊,例如特定使用者帳戶是否持續觸發錯誤,或是在其他網頁上執行的特定動作序列是否會觸發這個報表。

瀏覽器何時及如何傳送報表?

報表會從網站傳送至外部:瀏覽器會控制報表傳送至設定的端點的時間。也無法控制瀏覽器傳送報表的時間,因為瀏覽器會在適當時間自動擷取、排入佇列並傳送報表。

也就是說,使用 Reporting API 時,幾乎不會有任何效能問題。

報表會延遲傳送一段時間 (最多一分鐘),以提高批次傳送報表的機率。 這樣可以節省頻寬,以尊重使用者的網路連線,這在行動裝置中特別重要。如果瀏覽器忙著處理優先順序較高的工作,或是使用者的網路速度緩慢且/或壅塞,也可能導致傳送延遲。

第三方和第一方問題

系統會將網頁違規或淘汰情形產生的報表傳送至您設定的端點。這包括在您的網頁上執行的第三方指令碼所造成的違規行為。

在網頁中嵌入跨來源 iframe時發生的違規或淘汰情形,不會回報至端點 (至少在預設情況下不會)。iframe 可以設定自己的報表,甚至回報給您的網站 (即第一方) 報表服務;但這取決於被嵌入的網站。另請注意,大多數報告只有在違反網頁的政策時才會產生,且您網頁的政策和 iframe 的政策不同。

已淘汰項目的示例

如果網頁上已設定 Reporting-Endpoints 標頭,則在網頁上執行的第三方指令碼所呼叫的已淘汰 API 會回報至端點。嵌入網頁中的 iframe 呼叫的已淘汰 API 將不會回報至您的端點。只有在 iframe 伺服器已設定 Reporting-Endpoints 時,系統才會產生淘汰報表,且這份報表會傳送至 iframe 伺服器已設定的端點。
如果網頁上設有 Reporting-Endpoint 標頭:如果網頁上執行的第三方指令碼呼叫的已淘汰 API,則會回報至您的端點。網頁中嵌入的 iframe 呼叫的已淘汰 API 不會回報至端點。只有在 iframe 伺服器已設定 Reporting-Endpoints 時,系統才會產生淘汰報表,且這份報表會傳送至 iframe 伺服器已設定的端點。

瀏覽器支援

下表總結了 Reporting API v1 (即 Reporting-Endpoints 標頭) 的瀏覽器支援情形。瀏覽器支援的 Reporting API 第 0 版 (Report-To 標頭) 與舊版相同,但有一個例外:新版 Reporting API 不支援網路錯誤記錄。詳情請參閱遷移指南

報告類型 Chrome Chrome iOS 版 Safari Firefox Edge
CSP 違規 (僅限第 3 級)* ✔ 是 ✔ 是 ✔ 是 ✘ 否 ✔ 是
網路錯誤記錄 ✘ 否 ✘ 否 ✘ 否 ✘ 否 ✘ 否
違反 COOP/COEP ✔ 是 ✘ 否 ✔ 是 ✘ 否 ✔ 是
所有其他類型:違反政策、已淘汰、介入、異常終止 ✔ 是 ✘ 否 ✘ 否 ✘ 否 ✔ 是

這個表格只會概述 report-to 與新 Reporting-Endpoints 標頭的支援情形。如果您想遷移至 Reporting-Endpoints,請參閱內容供應器回報遷移秘訣

使用 Reporting API

決定要將報表傳送至何處

方法有以下兩種:

  • 將報表傳送至現有的報表收集器服務。
  • 將報表傳送至您自行建構及操作的報表收集器。

方法 1:使用現有的報表收集器服務

報表收集器服務的範例包括:

如果您知道其他解決方案,請開啟問題說明,我們會更新這篇文章。

除了定價之外,選擇報表收集工具時,也請考量下列幾點:🧐?

  • 這個收集器是否支援所有報表類型?舉例來說,並非所有報表端點解決方案都支援 COOP/COEP 報表。
  • 您是否願意將任何應用程式網址提供給第三方報表收集工具?即使瀏覽器會從這些網址中移除私密資訊,私密資訊仍可能因此外洩。如果這對應用程式來說風險太高,請自行操作回報端點。

方法 2:自行建構及操作報表收集器

自行建構用於接收報表的伺服器並非易事。首先,您可以對輕量的樣板分支這個平台是以 Express 建立,可接收及顯示報表。

  1. 前往範本報表收集器

  2. 按一下「Remix to Edit」,即可編輯專案。

  3. 您現在有本機副本了!您可以根據自身需求自訂。

如果您未使用範本,而是從頭開始建構自己的伺服器,請按照下列步驟操作:

  • 請檢查 POST 要求,並確認 Content-Typeapplication/reports+json,以便辨識瀏覽器傳送至端點的報表要求。
  • 如果端點位於與網站不同的來源,請確認該端點支援 CORS 預先檢查要求

選項 3:結合選項 1 和 2

您可能會想讓特定供應商處理某些類型的報表,但其他類型的報表則採用內部解決方案。

在這種情況下,請按照以下方式設定多個端點:

Reporting-Endpoints: endpoint-1="https://reports-collector.example", endpoint-2="https://my-custom-endpoint.example"

設定 Reporting-Endpoints 標頭

設定 Reporting-Endpoints 回應標頭。其值必須是一或多個以半形逗號分隔的鍵/值組合:

Reporting-Endpoints: main-endpoint="https://reports.example/main", default="https://reports.example/default"

如果您要從舊版 Reporting API 遷移至新版 Reporting API,建議您同時設定 Reporting-EndpointsReport-To。詳情請參閱遷移指南。特別是,如果您只透過 report-uri 指令回報 Content-Security-Policy 違規事項,請查看 回報 CSP 回報的遷移步驟

Reporting-Endpoints: main-endpoint="https://reports.example/main", default="https://reports.example/default"
Report-To: ...

鍵 (端點名稱)

每個鍵都可以是您選擇的名稱,例如 main-endpointendpoint-1。您可以為不同的報表類型設定不同名稱的端點,例如 my-coop-endpointmy-csp-endpoint。這樣一來,您就能根據報表類型將報表轉送至不同的端點。

如要接收介入淘汰和/或當機報表,請設定名為 default 的端點。

如果 Reporting-Endpoints 標頭未定義 default 端點,系統不會傳送這類報表 (但會產生這類報表)。

值 (網址)

每個值都是您選擇的網址,報表會傳送至該網址。您在這裡設定的網址取決於您在步驟 1 中決定的內容。

端點網址:

範例

Reporting-Endpoints: my-coop-endpoint="https://reports.example/coop", my-csp-endpoint="https://reports.example/csp", default="https://reports.example/default"

接著,您可以在適當的政策中使用每個已命名的端點,或是在所有政策中使用單一端點。

要設定標頭嗎?

在本文介紹的新版 Reporting API 中,報表的範圍是「文件」。也就是說,對於一個特定來源,不同的文件 (例如 site.example/page1site.example/page2) 可以將報表傳送至不同的端點。

如要接收網站任何網頁的違規或淘汰項目報表,請將所有回應的標頭設為中介軟體。

以下是 Express 中的範例:

const REPORTING_ENDPOINT_BASE = 'https://report.example';
const REPORTING_ENDPOINT_MAIN = `${REPORTING_ENDPOINT_BASE}/main`;
const REPORTING_ENDPOINT_DEFAULT = `${REPORTING_ENDPOINT_BASE}/default`;

app.use(function (request, response, next) {
  // Set up the Reporting API
  response.set(
    'Reporting-Endpoints',
    `main-endpoint="${REPORTING_ENDPOINT_MAIN}", default="${REPORTING_ENDPOINT_DEFAULT}"`,
  );
  next();
});

編輯政策

Reporting-Endpoints 標頭已設定完成,請在您想接收違規報告的每個政策標頭中,新增 report-to 指令。report-to 的值必須是你設定的其中一個已命名端點。

您可以為多項政策使用多個端點,也可以在多項政策中使用不同的端點。

每項政策的 report-to 值應為您已設定的命名端點之一。

report-to 不必用於淘汰介入當機報告。這些報告不受任何政策約束。只要設定 default 端點,系統就會產生這些檔案,並傳送至這個 default 端點。

範例

# Content-Security-Policy violations and Document-Policy violations
# will be sent to main-endpoint
Content-Security-Policy: script-src 'self'; object-src 'none'; report-to main-endpoint;
Document-Policy: document-write=?0;report-to=main-endpoint;
# Deprecation reports don't need an explicit endpoint because
# these reports are always sent to the default endpoint

範例程式碼

如要瞭解上述所有情況,以下為使用 Express 的節點伺服器範例,並且整合了本文討論的所有部分。這部影片將說明如何為多種不同報表類型設定報表,並顯示結果。

對報表設定進行偵錯

刻意產生報表

設定報表 API 時,您可能需要刻意違反政策,以便檢查系統是否會產生及傳送報表。如要查看違反政策的程式碼範例,以及會生成所有類型的報表,請查看示範

節省時間

報表可能會延遲傳送 (約 1 分鐘),這在偵錯時是長時間。😴? 幸好,在 Chrome 中偵錯時,您可以使用 --short-reporting-delay 標記,在報表產生後立即收到。

請在終端機中執行下列指令,開啟這個標記:

YOUR_PATH/TO/EXECUTABLE/Chrome --short-reporting-delay

使用開發人員工具

在 Chrome 中,使用開發人員工具查看已傳送或即將傳送的報表。

自 2021 年 10 月起,這項功能仍處於實驗階段。如要使用這項功能,請按照下列步驟操作:

  1. 使用 96 以上版本的 Chrome (在瀏覽器中輸入 chrome://version 即可確認)
  2. 在 Chrome 的網址列中輸入或貼上 chrome://flags/#enable-experimental-web-platform-features
  3. 按一下「已啟用」
  4. 重新啟動瀏覽器。
  5. 開啟 Chrome 開發人員工具。
  6. 在 Chrome 開發人員工具中開啟「設定」。在「實驗」下方,按一下「應用程式」面板中的「啟用報表 API」面板
  7. 重新載入開發人員工具。
  8. 重新載入網頁。開發人員工具頁面產生的報表會顯示在 Chrome 開發人員工具的「Applications」面板中,位於「Reporting API」下方。
開發人員工具列出報表的螢幕截圖
Chrome 開發人員工具會顯示網頁產生的報表和狀態。

報表狀態

「狀態」欄會指出報表是否已成功傳送。

狀態 說明
Success 瀏覽器已傳送報表,端點則以成功代碼 (200 或其他成功回應代碼 2xx) 回覆。
Pending 瀏覽器正在嘗試傳送報告。
Queued 報表已產生,但瀏覽器目前並未嘗試傳送。報表會以 Queued 的形式顯示,但只有在下列兩種情況下才會出現:
  • 此報表才剛推出,瀏覽器在嘗試傳送前,需先確認是否有更多報表送達。
  • 這份報表並非新報表,瀏覽器已嘗試傳送這份報表,但失敗,因此正在等待重新傳送。
MarkedForRemoval 重試一段時間後 (Queued),瀏覽器會停止嘗試傳送報表,並很快從要傳送的報表清單中移除報表。

無論報表是否成功傳送,系統都會在一段時間後移除報表。

疑難排解

報表是否無法產生或無法傳送至端點?以下提供一些疑難排解提示。

無法產生報表

開發人員工具中顯示的報表已正確產生。如果您要查看的報表顯示在清單中:

  • 請在政策中勾選 report-to。如果設定錯誤,系統不會產生報告請前往編輯政策進行修正。另一種排解這個問題的方法,是檢查 Chrome 中的開發人員工具主控台:如果主控台彈出錯誤訊息,指出您預期的違規行為,表示政策可能已正確設定。
  • 請注意,只有針對開啟的 DevTools 文件產生的報表才會顯示在這個清單中。舉例來說:如果您的網站 site1.example 嵌入違反政策的 iframe site2.example,因此產生報表,則只有在您在 iframe 專屬視窗中開啟 iframe,並為該視窗開啟開發人員工具時,這份報表才會顯示在開發人員工具中。

報表產生,但未傳送或未收到

如果開發人員工具中顯示報表,但端點未收到報告,該怎麼辦?

  • 請務必使用短暫延遲。您看不到報表的原因可能在於報表尚未傳送!
  • 請檢查 Reporting-Endpoints 標頭設定。如果發生問題,系統就不會傳送已正確產生的報表。在開發人員工具中,報表的狀態將保持為 Queued (在此情況下,可能會跳到 Pending,並在嘗試傳送時快速返回 Queued)。可能導致這個問題的常見錯誤:

  • 端點已使用,但未設定。範例:

錯誤程式碼
 Document-Policy: document-write=?0;report-to=endpoint-1;
 Reporting-Endpoints: default="https://reports.example/default"

文件政策違規報告應傳送至 endpoint-1,但這個端點名稱未在 Reporting-Endpoints 中設定。

  • 缺少 default 端點。部分報表類型 (例如淘汰和介入報表) 只會傳送至名為 default 的端點。詳情請參閱「設定 Reporting-Endpoints 標頭」。

  • 檢查政策標頭語法是否有問題,例如缺少引號。瞭解詳情

  • 確認端點可處理傳入的要求。

    • 請確認您的端點支援 CORS 預檢要求。如果沒有,就無法接收報表。

    • 測試端點的行為。為此,您可以模擬瀏覽器,向端點傳送「類似」瀏覽器會傳送的請求,而非手動產生報表。執行以下指令:

    curl --header "Content-Type: application/reports+json" \
      --request POST \
      --data '[{"age":420,"body":{"columnNumber":12,"disposition":"enforce","lineNumber":11,"message":"Document policy violation: document-write is not allowed in this document.","policyId":"document-write","sourceFile":"https://dummy.example/script.js"},"type":"document-policy-violation","url":"https://dummy.example/","user_agent":"xxx"},{"age":510,"body":{"blockedURL":"https://dummy.example/img.jpg","destination":"image","disposition":"enforce","type":"corp"},"type":"coep","url":"https://dummy.example/","user_agent":"xxx"}]' \
      YOUR_ENDPOINT
    

    端點應回傳成功代碼 (200 或其他成功回應代碼 2xx)。如果沒有,則表示設定有問題。

僅限報表

-Report-Only 政策標頭和 Reporting-Endpoints 可搭配運作。

Reporting-Endpoints 中設定的端點,以及在 Content-Security-PolicyCross-Origin-Embedder-PolicyCross-Origin-Opener-Policyreport-to 欄位中指定的端點,會在違反這些政策時收到回報。

您也可以在 Content-Security-Policy-Report-OnlyCross-Origin-Embedder-Policy-Report-OnlyCross-Origin-Opener-Policy-Report-Onlyreport-to 欄位中指定在 Reporting-Endpoints 中設定的端點。也會收到違規報告。

雖然這兩種情況都會傳送報表,但 -Report-Only 標頭不會強制執行政策:不會有任何作業中斷或實際上遭到封鎖,但您會收到報告,說明「可能」毀損或已封鎖的項目。

ReportingObserver

ReportingObserver JavaScript API 可協助您觀察用戶端警告。

ReportingObserverReporting-Endpoints 標頭產生的報表外觀相同,但用途略有不同。

使用 ReportingObserver 的情況:

  • 您只想監控淘汰和/或瀏覽器介入情形。ReportingObserver 會顯示用戶端警告,例如淘汰和瀏覽器介入,但與 Reporting-Endpoints 不同的是,它不會擷取任何其他類型的報表,例如 CSP 或 COOP/COEP 違規。
  • 請務必即時回應這些違規行為。ReportingObserver 可以將回呼附加到違規事件。
  • 您想透過自訂回呼,為報表附加其他資訊,以利偵錯。

另一個差異是 ReportingObserver 只會在用戶端設定:即使您無法控制伺服器端標頭,也無法設定 Reporting-Endpoints,還是可以使用 ReportingObserver

延伸閱讀

主頁橫幅圖片由 Nine Koepfer / @enka80 提供,Unsplash 圖片經過編輯。感謝 Ian Clelland、Eiji Kitamura 和 Milica Mihajlija 對本文的審查和建議。