Reporting API を使用してウェブ アプリケーションをモニタリングする

Reporting API を使用して、セキュリティ違反や非推奨の API 呼び出しなどをモニタリングします。

Maud Nalpas
Maud Nalpas

エラーの中には、本番環境でのみ発生するものもあります。実際のユーザー実際のネットワーク実際のデバイスによってゲームが変わるため、ローカルや開発中に確認することはできません。Reporting API は、セキュリティ違反や、サイト全体で非推奨になった API 呼び出しや間もなく非推奨になる API 呼び出しなど、こうしたエラーの一部を検出し、指定したエンドポイントに送信します。

HTTP ヘッダーを使用してモニタリングする内容を宣言でき、ブラウザによって動作します。

Reporting API を設定すると、ユーザーがこのようなエラーに遭遇したときに、そのことを把握して修正できるようになります。

この記事では、この API でできることと、その使用方法について説明します。それでは詳しく見ていきましょう。

概要

レポートの生成からデベロッパーによるレポートへのアクセスまでの手順をまとめた図
レポートの生成と送信の仕組み。

サイト site.example に Content-Security-Policy と Document-Policy があるとします。これらの機能が何をするものかわからない場合は、それでも、この例を理解することはできます。

これらのポリシーが違反されたタイミングを把握するため、また、コードベースで使用されている可能性のある非推奨の API や、まもなく非推奨になる 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.jsindex.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 のみ) ページの 1 つに 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 レポートの生成元となったドキュメントまたはワーカーのアドレス。ユーザー名、パスワード、フラグメントなどの機密データは、この 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 v1Reporting-Endpoints ヘッダーを使用)のブラウザ サポートをまとめたものです。Reporting API v0(Report-To ヘッダー)のブラウザ サポートは、1 つのレポートタイプを除いて同じです。新しい Reporting API では、ネットワーク エラー ロギングはサポートされていません。詳しくは、移行ガイドをご覧ください。

レポートの種類 Chrome Chrome iOS Safari Firefox Edge
CSP 違反(レベル 3 のみ)* ✔ はい ✔ はい ✔ はい ✘ いいえ ✔ はい
ネットワーク エラー ロギング ✘ いいえ ✘ いいえ ✘ いいえ ✘ いいえ ✘ いいえ
COOP/COEP 違反 ✔ はい ✘ いいえ ✔ はい ✘ いいえ ✔ はい
その他のタイプ: ドキュメント ポリシー違反、非推奨、介入、クラッシュ ✔ はい ✘ いいえ ✘ いいえ ✘ いいえ ✔ はい

この表は、新しい Reporting-Endpoints ヘッダーを使用した report-to のサポートのみをまとめたものです。Reporting-Endpoints への移行を検討している場合は、CSP レポートの移行に関するヒントをご覧ください。

Reporting API の使用

レポートの送信先を決定する

次のいずれかの方法でご対応ください。

  • 既存のレポート コレクタ サービスにレポートを送信します。
  • 自分で構築して運用するレポート コレクタにレポートを送信します。

オプション 1: 既存のレポート コレクタ サービスを使用する

レポート収集サービスには、次のようなものがあります。

他の解決策をご存知の場合は、問題を報告してください。この投稿を更新します。

レポート コレクタを選択する際は、料金に加えて次の点も考慮してください。🧐

  • このコレクタはすべてのレポートタイプをサポートしていますか?たとえば、すべてのレポート エンドポイント ソリューションが COOP/COEP レポートをサポートしているわけではありません。
  • アプリの URL を第三者のレポート収集ツールと共有することに抵抗はありますか?ブラウザがこれらの URL から機密情報を削除しても、この方法で機密情報が漏洩する可能性があります。アプリケーションにとってリスクが高すぎると思われる場合は、独自のレポート エンドポイントを運用してください。

オプション 2: 独自のレポート コレクタを構築して運用する

レポートを受信する独自のサーバーを構築するのは簡単ではありません。まず、軽量のボイラープレートをフォークします。Express で構築されており、レポートを受信して表示できます。

独自のレポート コレクタを構築する場合:

  • 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 レスポンス ヘッダーを設定します。値は、1 つまたは一連のカンマ区切りの Key-Value ペアにする必要があります。

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 エンドポイントが定義されていない場合、このタイプのレポートは送信されません(生成はされます)。

値(URL)

各値は、レポートの送信先となる任意の URL です。ここで設定する URL は、ステップ 1 で決定した内容によって異なります。

エンドポイント URL:

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

その後、各名前付きエンドポイントを適切なポリシーで使用するか、すべてのポリシーで 1 つのエンドポイントを使用できます。

ヘッダーの設定場所

この投稿で説明する新しい Reporting API では、レポートはドキュメントの範囲に限定されます。つまり、1 つのオリジンに対して、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 を設定する際は、レポートが期待どおりに生成されて送信されるかどうかを確認するために、ポリシーに意図的に違反する必要がある場合があります。

時間の節約

レポートは遅延して送信されることがあります(約 1 分)。これはデバッグ時には長い時間です。😴 幸いなことに、Chrome でデバッグを行う場合は、--short-reporting-delay フラグを使用して、レポートが生成されるとすぐにレポートを受け取ることができます。

ターミナルで次のコマンドを実行して、このフラグをオンにします。

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

DevTools を使用する

Chrome では、DevTools を使用して、送信済みまたは送信予定のレポートを確認します。

2021 年 10 月の時点で、この機能は試験運用版です。使用する手順は次のとおりです。

  1. Chrome バージョン 96 以降を使用する(ブラウザで chrome://version と入力して確認)
  2. Chrome のアドレスバーに chrome://flags/#enable-experimental-web-platform-features と入力するか、この URL をコピーして貼り付けます。
  3. [有効] をクリックします。
  4. ブラウザを再起動する。
  5. Chrome DevTools を開きます。
  6. Chrome DevTools で、[設定] を開きます。[試験運用版] で、[アプリケーション パネルで Reporting API パネルを有効にする] をクリックします。
  7. DevTools を再読み込みします。
  8. ページを再読み込みします。DevTools が開いているページで生成されたレポートは、Chrome DevTools の [アプリケーション] パネルの [Reporting API] に表示されます。
レポートを一覧表示する DevTools のスクリーンショット
Chrome DevTools に、ページで生成されたレポートとそのステータスが表示されます。

レポートのステータス

[ステータス] 列には、レポートが正常に送信されたかどうかが表示されます。

ステータス 説明
Success ブラウザがレポートを送信し、エンドポイントが成功コード(200 または別の成功レスポンス コード 2xx)で応答しました。
Pending ブラウザがレポートの送信を試みています。
Queued レポートは生成されていますが、ブラウザが送信を試みていません。レポートが Queued と表示されるのは、次の 2 つのケースのいずれかです。
  • レポートが新しく、ブラウザが送信を試みる前にさらにレポートが届くかどうかを待っている。
  • レポートは新しいものではありません。ブラウザはすでにこのレポートの送信を試みて失敗しており、再試行するまで待機しています。
MarkedForRemoval しばらく再試行(Queued)した後、ブラウザはレポートの送信を停止し、送信するレポートのリストからまもなく削除します。

レポートは、送信が成功したかどうかにかかわらず、しばらくすると削除されます。

トラブルシューティング

レポートが生成されない、または想定どおりにエンドポイントに送信されない。この問題を解決するためのヒントをいくつかご紹介します。

レポートが生成されない

DevTools に表示されるレポートは正しく生成されています。このリストに目的のレポートが表示されない場合は、次の手順を行います。

  • ポリシーで report-to を確認します。この設定が誤っていると、レポートは生成されません。この問題を解決するには、ポリシーを編集するをご覧ください。この問題をトラブルシューティングするもう 1 つの方法は、Chrome の DevTools コンソールを確認することです。コンソールに想定した違反のエラーが表示された場合は、ポリシーが正しく構成されている可能性があります。
  • このリストには、ドキュメントの DevTools が開いている状態で生成されたレポートのみが表示されます。たとえば、サイト site1.example がポリシーに違反する iframe site2.example を埋め込んでいて、レポートが生成された場合、このレポートは、iframe を独自のウィンドウで開き、そのウィンドウの DevTools を開いた場合にのみ DevTools に表示されます。

レポートは生成されたが、送信されない、または受信されない

DevTools にレポートが表示されるのに、エンドポイントでレポートを受信できない場合はどうすればよいですか?

  • 短い遅延を使用してください。レポートが表示されないのは、まだ送信されていないことが原因かもしれません。
  • Reporting-Endpoints ヘッダーの構成を確認します。問題がある場合、正しく生成されたレポートは送信されません。この場合、DevTools でレポートのステータスは 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 を使用すると、違反イベントにコールバックを添付できます。
  • カスタム コールバックを使用して、デバッグに役立つ追加情報をレポートに添付したい。

もう 1 つの違いは、ReportingObserver はクライアントサイドでのみ構成されることです。サーバーサイドのヘッダーを制御できず、Reporting-Endpoints を設定できない場合でも使用できます。

関連情報

ヒーロー画像: Nine Koepfer / @enka80Unsplash)を編集。このドキュメントのレビューと提案をしてくださった Ian Clelland 氏、北村英二氏、Milica Mihajlija 氏に感謝いたします。