使用 Reporting API 监控 Web 应用

使用 Reporting API 监控安全违规行为、已弃用的 API 调用等。

Maud Nalpas
Maud Nalpas

有些错误仅在生产环境中发生。您在本地或开发期间不会看到这些问题,因为真实用户真实网络真实设备会改变游戏。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 违规报告和弃用报告来捕获这些问题。

在短暂延迟(最多 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,该 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 生成报告所依据的文档或工作线程的地址。系统会从相应网址中剥离用户名、密码和 fragment 等敏感数据
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 构建,可以接收和显示报告。

在构建自己的报告收集器时:

  • 检查 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-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

调试报告设置

有意生成报告

设置 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 开发者工具的应用面板中的 Reporting API 下。
DevTools 列出报告的屏幕截图
Chrome 开发者工具会显示网页上生成的报告及其状态。

报告状态

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

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

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

问题排查

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

未生成报告

在开发者工具中显示的报告已正确生成。如果您期望的报告显示在此列表中,请执行以下操作:

  • 检查政策中的 report-to。如果此设置配置错误,系统将不会生成报告。前往修改政策以解决此问题。另一种问题排查方法是检查 Chrome 中的开发者工具控制台:如果控制台中弹出您预期的违规错误,则表示您的政策可能已正确配置。
  • 请注意,此列表中只会显示为打开了开发者工具的文档生成的报告。举例来说,如果您的网站 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"

Document-Policy 违规报告应发送到 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 字段中指定的端点,会在这些政策遭到违反时收到报告。

Reporting-Endpoints 中配置的端点也可以在 Content-Security-Policy-Report-OnlyCross-Origin-Embedder-Policy-Report-OnlyCross-Origin-Opener-Policy-Report-Onlyreport-to 字段中指定。如果违反了这些政策,他们还会收到报告。

虽然在这两种情况下都会发送报告,但 -Report-Only 标头不会强制执行政策:不会出现任何中断或实际屏蔽,但您会收到有关可能中断或被屏蔽的内容的报告。

ReportingObserver

ReportingObserver JavaScript API 可帮助您观察客户端警告。

ReportingObserverReporting-Endpoints 标题生成的报告看起来相同,但它们支持的使用场景略有不同。

如果出现以下情况,请使用 ReportingObserver

  • 您只想监控弃用或浏览器干预。 ReportingObserver 会显示客户端警告,例如弃用和浏览器干预,但与 Reporting-Endpoints 不同,它不会捕获任何其他类型的报告,例如 CSP 或 COOP/COEP 违规。
  • 您需要实时应对这些违规行为。借助 ReportingObserver,您可以将回调附加到违规事件。
  • 您希望使用自定义回调将其他信息附加到报告中,以帮助进行调试。

另一个区别是,ReportingObserver 仅在客户端配置:即使您无法控制服务器端标头,也无法设置 Reporting-Endpoints,也可以使用它。

深入阅读

主打图片由 Nine Koepfer / @enka80 拍摄,选自 Unsplash,经过修改。非常感谢 Ian Clelland、Eiji Kitamura 和 Milica Mihajlija 对本文档的审核和建议。