使用 Reporting API 监控 Web 应用

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

Maud Nalpas
Maud Nalpas

有些错误只发生在生产环境中。您不会在本地或开发期间看到它们,因为真实用户、真实网络和真实设备会改变游戏规则。Reporting API 有助于捕获其中一些错误(例如整个网站中的安全违规或已弃用和即将弃用的 API 调用),并将其传输到您指定的端点。

它允许您通过 HTTP 标头声明要监控的内容,并且由浏览器操作。

设置 Reporting API 后,您就会知道,当用户遇到这些类型的错误时,可以进行修复,这让您高枕无忧。

这篇博文将介绍此 API 的功能和用法。那就进入正题吧!

演示和代码

查看 Reporting API 从 Chrome 96 及更高版本(自 2021 年 10 月起适用于 Chrome Beta 版或 Canary 版)的实际应用。

概览

该示意图总结了从生成报告到开发者访问报告所需执行的步骤
如何生成报告和发送报告。

假设您的网站 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 已弃用或即将被弃用;它直接调用或通过顶级第三方脚本调用。
干预 出于安全性、性能或用户体验方面的原因,该网页正试图执行浏览器决定不遵从的操作。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-Endpoint 标头:系统会将网页上运行的第三方脚本调用的已弃用 API 报告给您的端点。由网页中嵌入的 iframe 调用的已弃用 API 不会报告给您的端点。仅当 iframe 服务器设置了 Reporting-Endpoint 时,系统才会生成弃用报告,并且系统会将此报告发送到 iframe 服务器设置的任何端点。
如果您的网页上设置了 Reporting-Endpoint 标头:系统会将网页上运行的第三方脚本调用的已弃用 API 报告给您的端点。由网页中嵌入的 iframe 调用的已弃用 API 不会报告给您的端点。仅当 iframe 服务器设置了 Reporting-Endpoint 时,系统才会生成弃用报告,并且系统会将此报告发送到 iframe 服务器设置的任何端点。

浏览器支持

下表总结了浏览器对 Reporting API v1 的支持情况,即使用 Reporting-Endpoints 标头。浏览器对 Reporting API v0(Report-To 标头)的支持相同,但有一种报告类型除外:新的 Reporting API 不支持网络错误日志记录。如需了解详情,请参阅迁移指南

报告类型 Chrome iOS 版 Chrome 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. 现在,您有自己的克隆人了!您可以根据自己的需要对其进行自定义。

如果您未使用样板代码,而是从头开始构建自己的服务器:

  • 检查 Content-Typeapplication/reports+jsonPOST 请求,以识别浏览器向端点发送的报告请求。
  • 如果您的端点与您的网站位于不同的来源,请确保它支持 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(这篇博文介绍的 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. 重新加载开发者工具。
  8. 重新加载页面。打开开发者工具页面生成的报告将列在 Chrome 开发者工具的 Application 面板中的 Reporting API 下。
列出报告的开发者工具的屏幕截图
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 的端点。如需了解详情,请参阅配置报告端点标头

  • 查找政策标头语法中是否存在问题,例如缺少引号。查看详情

  • 检查您的端点是否可以处理传入请求。

    • 确保您的端点支持 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,也可以使用它。

深入阅读

主打图片,创作者:Nine Koepfer / @enka80Unsplash 用户,已编辑。非常感谢 Ian Clelland、Eiji Kitamura 和 Milica Mihajlija 对本文的评价和建议。