使用 Reporting API 监控 Web 应用

使用 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 包含 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 违规报告、文档政策违规报告和弃用报告,以捕获这些问题。

然后,浏览器会在短暂延迟(最长 1 分钟)后将报告发送到为此违规类型配置的端点。报告由浏览器本身(而非您的服务器或网站)非正规渠道发送。

端点会接收这些报告。

您现在可以访问这些端点的报告,并监控出现的问题。 现在,您可以开始排查影响用户的问题了。

示例报告

{
  "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-violationcoep
url 生成报告时所依据的文档或工人的地址。系统会从此网址中移除用户名、密码和片段等敏感数据
user_agent 生成报告的请求的 User-Agent 标头。

经过认证的报告

与生成报告的网页具有相同来源的报告端点会在包含报告的请求中接收凭据(Cookie)。

凭据可能会提供有关报告的有用背景信息,例如指定用户的账号是否一直在触发错误,或者在其他网页上执行的特定操作序列是否会触发此网页上的报告。

浏览器何时以及如何发送报告?

报告会从您的网站异步传送:浏览器会控制报告何时发送到配置的端点。此外,您也无法控制浏览器发送报告的时间;它会在合适的时间捕获、加入队列并自动发送报告。

这意味着,使用 Reporting API 时,性能问题微乎其微。

报告会延迟发送(最长延迟 1 分钟),以提高批量发送报告的几率。这可以节省带宽,以尊重用户的网络连接,这一点在移动设备上尤为重要。如果浏览器正忙于处理更高优先级的工作,或者用户当前所用的网络速度缓慢且/或网络拥塞,浏览器也可能会延迟提交。

第三方和第一方问题

由于您的网页上发生违规或弃用问题而生成的报告将发送到您配置的端点。这包括您网页上运行的第三方脚本所犯的违规行为。

在网页中嵌入的跨源 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 标头的 API)的支持情况。浏览器对 Reporting API v0(Report-To 标头)的支持保持不变,但有一个报告类型除外:新版 Reporting API 不支持网络错误日志记录。如需了解详情,请参阅迁移指南

报告类型 Chrome Chrome iOS Safari Firefox Edge
CSP 违规(仅限第 3 级)* ✔ 是 ✔ 是 ✔ 是 ✘ 否 ✔ 是
网络错误日志记录 ✘ 否 ✘ 否 ✘ 否 ✘ 否 ✘ 否
违反 COOP/COEP 政策 ✔ 是 ✘ 否 ✔ 是 ✘ 否 ✔ 是
所有其他类型:违反文档政策、弃用、干预、崩溃 ✔ 是 ✘ 否 ✘ 否 ✘ 否 ✔ 是

此表仅总结了对带有新 Reporting-Endpoints 标头的 report-to 的支持。如果您想要迁移到 Reporting-Endpoints,请参阅 CSP 报告迁移提示

使用 Reporting API

确定应将报告发送到何处

您有两种选择:

  • 将报告发送到现有报告收集器服务。
  • 将报告发送到您自行构建和运营的报告收集器。

方案 1:使用现有的报告收集器服务

报告收集器服务的示例包括:

如果您知道其他解决方法,请提交问题告诉我们,我们会更新这篇文章!

选择报告收集器时,除了价格之外,还应考虑以下几点

  • 此收集器是否支持所有报告类型?例如,并非所有报告端点解决方案都支持 COOP/COEP 报告。
  • 您是否愿意与第三方报告收集器分享应用的任何网址? 即使浏览器从这些网址中移除敏感信息,敏感信息也可能会以这种方式泄露。如果这对您的应用来说风险太高,请自行运营报告端点。

方法 2:自行构建和运行报告收集器

自行构建用于接收报告的服务器并非易事。首先,您可以分叉我们的轻量级样板。该应用是使用 Express 构建的,可以接收和显示报告。

  1. 前往样本报告收集器

  2. 点击 Remix to Edit 即可修改项目。

  3. 现在,您已经拥有自己的克隆了!您可以根据自己的用途对其进行自定义。

如果您不使用样板,而是从头开始构建自己的服务器,请执行以下操作:

  • 检查 POST 请求是否具有 application/reports+jsonContent-Type,以识别浏览器向您的端点发送的报告请求。
  • 如果您的端点位于与您的网站不同的源,请确保它支持 CORS 预检请求

方案 3:结合使用方案 1 和方案 2

您可能希望让特定提供商负责处理某些类型的报告,但自行处理其他类型的报告。

在这种情况下,请按如下方式设置多个端点:

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

配置 Reporting-Endpoints 标头

设置 Reporting-Endpoints 响应标头。其值必须是 1 个或一系列以英文逗号分隔的键值对:

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(本文将介绍该 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 的 Node 服务器示例,其中汇集了本文中讨论的所有部分。该视频展示了如何为几种不同的报告类型配置报告,并显示了结果。

调试报告设置

有意生成报告

设置 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. 重新加载 DevTools。
  8. 重新加载页面。开发者工具所在网页生成的报告会列在 Chrome DevTools 的 Application 面板中的 Reporting API 下。
列出报告的 DevTools 屏幕截图
Chrome 开发者工具会显示在您的网页上生成的报告及其状态。

报告状态

“状态”列会显示报告是否已成功发送。

状态 说明
Success 浏览器已发送报告,并且端点回复了成功代码(200 或其他成功响应代码 2xx)。
Pending 浏览器目前正在尝试发送报告。
Queued 报告已生成,并且浏览器目前不会尝试发送该报告。报告会在以下两种情况下显示为 Queued
  • 该报告是新报告,浏览器会等待更多报告到达后,再尝试发送。
  • 该报告并非新报告;浏览器已尝试发送此报告,但未能成功,正在等待重试。
MarkedForRemoval 在重试了一段时间 (Queued) 后,浏览器已停止尝试发送报告,并将很快将其从要发送的报告列表中移除。

无论报告是否成功发送,系统都会在一段时间后将其移除。

问题排查

报告未按预期生成或未发送到您的端点? 以下是排查此问题的一些提示。

未生成报告

在 DevTools 中显示的报告已正确生成。如果您预期的报告显示在此列表中,请执行以下操作:

  • 检查您的政策中的 report-to。如果配置错误,系统将不会生成报告。请前往修改您的政策以解决此问题。排查此问题的另一种方法是检查 Chrome 中的 DevTools 控制台:如果控制台中弹出与您预期的违规行为相关的错误,则表示您的政策可能已正确配置。
  • 请注意,只有针对 DevTools 打开的文档生成的报告才会显示在此列表中。举个例子:如果您的网站 site1.example 嵌入了违反政策的 iframe site2.example,从而生成了报告,那么只有当您在自己的窗口中打开该 iframe 并为该窗口打开 DevTools 时,该报告才会显示在 DevTools 中。

报告已生成,但未发送或未收到

如果您可以在 DevTools 中看到报告,但您的端点未收到该报告,该怎么办?

  • 请务必使用短延迟。您看不到报告可能是因为报告尚未发送!
  • 检查您的 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

深入阅读

主打图片由 Unsplash 上的 Nine Koepfer / @enka80 提供,已经修改。非常感谢 Ian Clelland、Eiji Kitamura 和 Milica Mihajlija 对本文的审核和建议。