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

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

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

這項功能可讓您使用 HTTP 標頭宣告要監控的項目,並由瀏覽器操作。

設定 Reporting API 後,您就能在使用者遇到這類錯誤時收到通知,進而修正問題。

這篇文章將說明這項 API 的功能和使用方式。讓我們深入瞭解!

總覽

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

假設您的網站 (site.example) 具有 Content-Security-Policy 和 Document-Policy。不瞭解這些功能的用途嗎?沒關係,您還是可以瞭解這個範例。

您決定監控網站,以便瞭解何時違反這些政策,同時也想留意程式碼集可能使用的已淘汰或即將淘汰的 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 違規報告、Document-Policy 違規報告和淘汰報告,記錄這些問題。

瀏覽器會稍作延遲 (最多一分鐘),然後將報表傳送到為這類違規事項設定的端點。報表會由瀏覽器本身 (而非伺服器或網站) 頻外傳送。

端點會接收這些報表。

現在可以存取這些端點的報告,並監控發生的錯誤。您可以開始排解影響使用者的問題。

範例報表

{
  "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,或使用頂層第三方指令碼呼叫該 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-violation 或 coep。
url 產生報表的文件或 worker 位址。使用者名稱、密碼和片段等私密資料會從這個網址中移除。
user_agent 產生報表的要求的 User-Agent 標頭。

憑證報表

如果回報端點與產生報表的網頁來源相同,則會在包含報表的要求中收到憑證 (Cookie)。

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

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

報表會透過網站頻外傳送:瀏覽器會控管報表傳送至已設定端點的時間。此外,您也無法控制瀏覽器傳送報表的時間,瀏覽器會自動擷取、排入佇列,並在適當時間傳送報表。

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

報表會延遲傳送 (最多一分鐘),以提高分批傳送報表的機率。這樣做可節省頻寬,避免耗用使用者網路連線的流量,這對行動裝置尤其重要。如果瀏覽器正在處理優先順序較高的工作,或使用者當時使用的網路速度緩慢或壅塞,瀏覽器也可能延遲傳送。

第三方和第一方問題

如果網頁發生違規或淘汰情形,系統會產生報表,並傳送至您設定的端點。包括網頁上執行的第三方指令碼違規。

如果違規或淘汰情形發生在網頁嵌入的跨源 iframe,系統不會回報給端點 (至少預設不會)。iframe 可以自行設定報表,甚至向您網站 (也就是第一方) 的報表服務回報,但這取決於框架網站。另請注意,只有在違反網頁政策時,系統才會產生大多數報表,且網頁政策和 iframe 政策不同。

含有淘汰項目的範例

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

瀏覽器支援

下表匯總了瀏覽器對 Reporting API v1 (即 Reporting-Endpoints 標頭) 的支援。除了「網路錯誤記錄」報表類型外,瀏覽器對 Reporting API v0 (Report-To 標頭) 的支援情形相同。新版 Reporting API 不支援「網路錯誤記錄」報表類型。詳情請參閱遷移指南。

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

這個表格僅摘要說明新 Reporting-Endpoints 標頭對 report-to 的支援。如要遷移至 Reporting-Endpoints,請參閱 CSP 報告遷移提示。

使用 Reporting API

決定報表傳送位置

方法有以下兩種:

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

選項 1:使用現有的報表收集器服務

以下是幾個報表收集器服務範例:

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

除了價格之外,選取報表收集器時,請考量下列幾點:🧐

  • 這個收集器是否支援所有報表類型?舉例來說,並非所有回報端點解決方案都支援 COOP/COEP 報表。
  • 你是否願意與第三方報表收集者分享應用程式的網址?即使瀏覽器從這些網址中移除私密資訊,私密資訊仍可能透過這種方式外洩。如果這對您的應用程式來說風險太高,請自行運作回報端點。

選項 2:建構及操作自己的報表收集器

自行建構接收報表的伺服器並非易事。如要開始,您可以建立輕量樣板的分支版本。這個應用程式是以 Express 建構,可接收及顯示報表。

建立自己的報表收集器時,請注意下列事項:

  • 檢查 POST 要求是否含有 Content-Type,以辨識瀏覽器傳送至端點的報告要求。application/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-Endpoints 和 Report-To。詳情請參閱遷移指南。特別是,如果您只使用 report-uri 指令回報 Content-Security-Policy 違規事項,請參閱 CSP 回報功能的遷移步驟。

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

鍵 (端點名稱)

每個鍵都可以是您選擇的名稱,例如 main-endpoint 或 endpoint-1。您可以決定為不同報表類型設定不同的具名端點,例如 my-coop-endpoint、my-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/page1 和 site.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

偵錯報表設定

刻意產生報表

設定 Reporting API 時,您可能需要刻意違反政策,檢查報表是否能正常產生及傳送。

節省時間

報告可能會延遲傳送 (約一分鐘),這在偵錯時是很長的時間。😴 幸好,在 Chrome 中進行偵錯時,您可以使用 --short-reporting-delay 旗標,在報表生成後立即收到。

在終端機中執行下列指令,啟用這個旗標:

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

使用開發人員工具

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

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

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

報表狀態

「狀態」欄會顯示檢舉是否已成功送出。

狀態 說明
Success 瀏覽器已傳送報表,端點則以成功代碼 (200 或其他成功回應代碼 2xx) 回覆。
Pending 瀏覽器正在嘗試傳送報告。
Queued 報表已產生,瀏覽器不會嘗試傳送。在下列兩種情況下,報表會顯示為 Queued:
  • 報告是新建立的,瀏覽器正在等待是否還有其他報告,再嘗試傳送。
  • 這份報告並非新報告;瀏覽器先前已嘗試傳送這份報告,但失敗了,目前正在等待再次嘗試。
MarkedForRemoval 經過一段時間 (Queued) 後,瀏覽器已停止嘗試傳送報告,並即將從待傳送報告清單中移除該報告。

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

疑難排解

報表是否未產生,或未如預期傳送至端點?以下提供幾個疑難排解訣竅。

無法產生報表

開發人員工具中顯示的報表已正確產生。如果清單中未顯示您預期的報表:

  • 請查看政策中的 report-to。如果設定錯誤,系統就不會產生報表。請前往「編輯政策」修正問題。如要進一步排解這個問題,請檢查 Chrome 中的開發人員工具控制台:如果控制台針對您預期的違規事項彈出錯誤訊息,表示您的政策可能已正確設定。
  • 請注意,這份清單只會顯示在開啟開發人員工具的文件中產生的報表。舉例來說,如果您的網站 site1.example 嵌入了違反政策的 iframe site2.example,因而產生報表,則只有在您於獨立視窗中開啟 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-Policy、Cross-Origin-Embedder-Policy 和 Cross-Origin-Opener-Policy 的 report-to 欄位中指定的端點,會在違反這些政策時收到報告。

在 Reporting-Endpoints 中設定的端點也可以在 Content-Security-Policy-Report-Only、Cross-Origin-Embedder-Policy-Report-Only 和 Cross-Origin-Opener-Policy-Report-Only 的 report-to 欄位中指定。如果有人違反這些政策,他們也會收到報告。

雖然這兩種情況都會傳送報表,但 -Report-Only 標頭不會強制執行政策:不會有任何內容遭到封鎖或中斷,但您會收到報表,瞭解哪些內容可能遭到封鎖或中斷。

ReportingObserver

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

ReportingObserver 和 Reporting-Endpoints 標頭產生的報表看起來相同,但用途略有不同。

在下列情況下使用 ReportingObserver:

  • 您只想監控淘汰或瀏覽器干預措施。 ReportingObserver 會顯示用戶端警告,例如淘汰和瀏覽器介入,但與 Reporting-Endpoints 不同,它不會擷取任何其他類型的報告,例如 CSP 或 COOP/COEP 違規。
  • 你必須即時處理這些違規事項。ReportingObserver 可讓您將回呼附加至違規事件。
  • 您想在報表中附加額外資訊,以便使用自訂回呼進行偵錯。

另一個差異是,ReportingObserver 只能在用戶端設定:即使您無法控管伺服器端標頭,也無法設定 Reporting-Endpoints,仍可使用這項功能。

延伸閱讀

非常感謝 Ian Clelland、Eiji Kitamura 和 Milica Mihajlija 對本文件的評論和建議。