Chrome DevTools での CSP と Trusted Types のデバッグの実装

Kateryna Prokopenko
Kateryna Prokopenko
Alfonso Castaño
Alfonso Castaño

このブログ投稿では、最近導入された [問題] タブを利用して、コンテンツ セキュリティ ポリシー(CSP)の問題をデバッグするための DevTools のサポートの実装について説明します。

実装作業は 2 件のインターンシップで実施しました。 1.最初のテストでは、一般的な報告フレームワークを構築し、CSP 違反 3 件の問題メッセージを設計しました。 2. 2 回目の投稿では、Trusted Type に関する問題と、Trusted Types をデバッグするための特別な DevTools 機能をいくつか追加しました。

コンテンツ セキュリティ ポリシーとは

コンテンツ セキュリティ ポリシー(CSP)を使用すると、ウェブサイトでの特定の動作を制限してセキュリティを強化できます。たとえば、CSP を使用してインライン スクリプトや eval を禁止することで、クロスサイト スクリプティング(XSS)攻撃の攻撃対象領域を減らすことができます。CSP の詳細については、こちらをご覧ください。

特に新しい CSP が Trusted Types(TT)ポリシーです。このポリシーは動的分析を可能にすることで、ウェブサイトに対する大規模なインジェクション攻撃を体系的に防止できます。これを実現するために、TT では、innerHTML などの DOM シンクに特定の種類のもののみを割り当てるよう JavaScript コードを管理できるウェブサイトをサポートしています。

ウェブサイトで特定の HTTP ヘッダーを含めることで、コンテンツ セキュリティ ポリシーを有効にできます。たとえば、ヘッダーには content-security-policy: require-trusted-types-for 'script'; trusted-types default ページの TT ポリシーが有効になるように設定します。

各ポリシーは、次のいずれかのモードで動作します。

  • 自動適用モード - すべてのポリシー違反がエラーになります。
  • レポート専用モード - エラー メッセージを警告として報告しますが、ウェブページでのエラーは発生しません。

[問題] タブでのコンテンツ セキュリティ ポリシーに関する問題の実装

この取り組みの目標は、CSP の問題に対するデバッグ エクスペリエンスを改善することでした。新しい問題を検討する際、DevTools チームはおおむねこのプロセスに従います。

  1. ユーザー ストーリーの定義。DevTools フロントエンドで、ウェブ デベロッパーがどのように問題を調査する必要があるかを説明する一連のユーザー ストーリーを特定します。
  2. フロントエンドの実装。ユーザー ストーリーに基づいて、フロントエンドで問題の調査に必要な情報(関連するリクエスト、Cookie の名前、スクリプトまたは html ファイルの行など)を特定します。
  3. 問題の検出。Chrome で問題を検出できるブラウザ内の場所を特定し、ステップ(2)の関連情報を含む問題を報告する場所を設定します。
  4. 問題を保存して表示します。問題を適切な場所に保存し、開いたら DevTools で使用できるようにします
  5. 問題文を設計する。ウェブ デベロッパーが問題を理解し、より重要な修正に役立つ説明テキストを作成する

ステップ 1: CSP の問題に関するユーザー ストーリーを定義する

実装作業を開始する前に、必要な作業をより深く理解するために、ユーザー ストーリーを含む設計ドキュメントを作成しました。たとえば、次のようなユーザー ストーリーを書きました。


ウェブサイトの一部がブロックされていることに気づいたデベロッパーとして、次のことをしたいです。 - ウェブサイトで iframe / 画像がブロックされる原因が CSP にあるかどうかを確認する - どの CSP ディレクティブが特定のリソースのブロックを引き起こしているかを知る - 現在ブロックされているリソースの表示や現在ブロックされている JS の実行を許可するように、ウェブサイトの CSP を変更する方法を知る。


このユーザー ストーリーを調べるために、私たちが関心を寄せた CSP 違反を示す簡単なウェブページの例をいくつか作成し、サンプル ページを見てそのプロセスに慣れました。 ウェブページの例を次に示します([問題] タブを開いた状態でデモを開きます)。

このプロセスから、ソースの場所が CSP の問題をデバッグするうえで最も重要な情報であることがわかりました。また、リソースがブロックされた場合に、関連する iframe とリクエストをすばやく見つけることも、DevTools の [Elements] パネルにある HTML 要素への直接リンクも有用であることも確認しました。

ステップ 2: フロントエンドの実装

この分析情報を、Chrome DevTools Protocol(CDP)経由で DevTools で利用できるようにしたいと考えている情報の最初のドラフトにしました。

以下は、third_party/blink/public/devtools_protocol/browser_protocol.pdl からの抜粋です。

 type ContentSecurityPolicyIssueDetails extends object
   properties
     # The url not included in allowed sources.
     optional string blockedURL
     # Specific directive that is violated, causing the CSP issue.
     string violatedDirective
     boolean isReportOnly
     ContentSecurityPolicyViolationType contentSecurityPolicyViolationType
     optional AffectedFrame frameAncestor
     optional SourceCodeLocation sourceCodeLocation
     optional DOM.BackendNodeId violatingNodeId

上記の定義は、基本的に JSON データ構造をエンコードしたものです。これは PDL(プロトコル データ言語)と呼ばれるシンプルな言語で記述されています。PDL は 2 つの目的で使用されます。まず、PDL を使用して、DevTools のフロントエンドが依存する TypeScript 定義を生成します。たとえば、上記の PDL 定義では、次の TypeScript インターフェースが生成されます。

export interface ContentSecurityPolicyIssueDetails {
  /**
  * The url not included in allowed sources.
  */
  blockedURL?: string;
  /**
  * Specific directive that is violated, causing the CSP issue.
  */
  violatedDirective: string;
  isReportOnly: boolean;
  contentSecurityPolicyViolationType: ContentSecurityPolicyViolationType;
  frameAncestor?: AffectedFrame;
  sourceCodeLocation?: SourceCodeLocation;
  violatingNodeId?: DOM.BackendNodeId;
}

次に、おそらくより重要なこととして、C++ Chromium バックエンドから DevTools フロントエンドへのこれらのデータ構造の生成と送信を処理する定義から C++ ライブラリを生成します。このライブラリを使用し、次の C++ コードを使用して ContentSecurityPolicyIssueDetails オブジェクトを作成できます。

protocol::Audits::ContentSecurityPolicyIssueDetails::create()
  .setViolatedDirective(d->violated_directive)
  .setIsReportOnly(d->is_report_only)
  .setContentSecurityPolicyViolationType(BuildViolationType(
      d->content_security_policy_violation_type)))
  .build();

どの情報を公開するかが決まったら、Chromium でその情報をどこで入手できるかを調べる必要がありました。

ステップ 3: 問題の検出

前のセクションで説明した形式で Chrome DevTools Protocol(CDP)で情報を利用できるようにするには、バックエンドで実際に情報を利用できる場所を見つける必要がありました。幸いなことに、CSP コードにはレポート専用モードにすでにボトルネックがあり、ContentSecurityPolicy::ReportViolationCSP HTTP ヘッダーで構成できるレポート エンドポイント(オプション)に問題を報告しています。私たちが報告したいと考えていた情報のほとんどは、すでに利用可能になっているため、計測を機能させるためにバックエンドに大きな変更を加える必要はありませんでした。

ステップ 4: 問題を保存して表示する

少し複雑になっているのは、DevTools が開かれる前に発生した問題を報告したいと考えていたことです(コンソール メッセージの処理方法と同様)。つまり、問題をフロントエンドにすぐに報告するのではなく、DevTools が開いているかどうかに関係なく、問題が格納されたストレージを使用します。DevTools を開くと(または、他の CDP クライアントが接続されている場合)、以前に記録されたすべての問題をストレージから再生できます。

これでバックエンドの作業は終了し、フロントエンドで問題を特定する方法に重点を置く必要がありました。

ステップ 5: 問題テキストを設計する

問題文の設計は、Google 自体以外にも複数のチームが関与するプロセスです。たとえば、機能を実装するチーム(この場合は CSP チーム)や、ウェブ デベロッパーが特定の種類の問題にどのように対処すべきかを設計する DevRel チームの分析情報を利用することがよくあります。問題文は通常、完成するまで修正を加えます。

通常、DevTools チームは、想定される以下の大まかな下書きから始めます。


## Header
Content Security Policy: include all sources of your resources in content security policy header to improve the functioning of your site

## General information
Even though some sources are included in the content security policy header, some resources accessed by your site like images, stylesheets or scripts originate from sources not included in content security policy directives.

Usage of content from not included sources is restricted to strengthen the security of your entire site.

## Specific information

### VIOLATED DIRECTIVES
`img-src 'self'`

### BLOCKED URLs
https://imgur.com/JuXCo1p.jpg

## Specific information
https://web.dev/strict-csp/

繰り返し処理した結果、次の結果が得られました。

ALT_TEXT_HERE

ご覧のとおり、機能チームと DevRel が関与することで、説明がより明確で正確なものになりました。

ページに表示された CSP の問題は、CSP 違反専用のタブでも見つけることができます。

Trusted Types の問題のデバッグ

TT を大規模に運用するのは、適切なデベロッパー ツールがないと困難な場合があります。

コンソール プリントの改善

信頼されているオブジェクトを操作するときは、少なくとも信頼されていないオブジェクトと同じ量の情報を表示する必要があります。残念ながら、現時点では、信頼できるオブジェクトを表示するときに、ラップされたオブジェクトに関する情報は表示されません。

これは、コンソールに表示される値が、デフォルトでオブジェクトの .valueOf() の呼び出しから取得されるためです。ただし、Trusted Types の場合、戻り値はあまり役に立ちません。代わりに、.toString() を呼び出したときに得られるものと同様のものを用意したいと思います。これを実現するには、V8 と Blink に変更を加えて、信頼できるタイプのオブジェクトに対する特別な処理を導入する必要があります。

このカスタム処理は V8 で行われた歴史的な理由から、この方法には重大な欠点があります。カスタム表示が必要なオブジェクトも多数ありますが、そのオブジェクトの型は JS レベルでは同じです。V8 は純粋な JS であるため、Trusted Type などのウェブ API に対応するコンセプトを区別できません。そのため、V8 は埋め込みツール(Blink)に両者を区別してもらう必要があります。

したがって、コードの該当部分を Blink や任意の埋め込みツールに移動するのは当然の選択に思えます。明らかになった問題以外にも、次のようなメリットがあります。

  • 各埋め込み機能では、独自の説明生成と
  • Blink API を使用すると説明を簡単に生成できる
  • Blink はオブジェクトの元の定義にアクセスできます。したがって、.toString() を使用して説明を生成する場合、.toString() が再定義されるリスクはありません。

違反行為(レポート専用モード)

現在、TT 違反をデバッグする唯一の方法は、JS 例外にブレークポイントを設定することです。TT 違反が適用されると例外がトリガーされるため、この機能はなんらかの形で役立ちます。ただし、実際のシナリオでは、TT 違反をより細かく制御する必要があります。特に、TT 違反のみ(他の例外は除く)で中断し、レポート専用モードでも中断し、さまざまな種類の TT 違反を区別したいと考えています。

DevTools はすでにさまざまなブレークポイントをサポートしているため、このアーキテクチャは拡張性に優れています。新しいブレークポイント タイプを追加するには、バックエンド(Blink)、CDP、フロントエンドで変更を加える必要があります。 setBreakOnTTViolation という新しい CDP コマンドを導入する必要があります。このコマンドはフロントエンドによって使用され、どのような種類の TT 違反を修復する必要があるかをバックエンドに伝えます。バックエンド(特に InspectorDOMDebuggerAgent)は、TT 違反が発生するたびに呼び出される「プローブ」である onTTViolation() を提供します。次に、InspectorDOMDebuggerAgent は、その違反によってブレークポイントがトリガーされるかどうかを確認し、トリガーする場合は、フロントエンドにメッセージを送信して実行を一時停止します。

完了内容と次のステップ

ここで説明する問題の導入後に、[問題] タブが大幅に変更されました。

今後は、[問題] タブを使用してさらに多くの問題を表示させる予定です。これにより、長期的には、Google Play Console で判読不能なエラー メッセージ フローをアンロードできるようになります。

プレビュー チャンネルをダウンロードする

デフォルトの開発ブラウザとして Chrome の CanaryDev、または Beta を使用することを検討してください。これらのプレビュー チャンネルを使用すると、DevTools の最新機能にアクセスしたり、最先端のウェブ プラットフォーム API をテストしたり、ユーザーに先駆けてサイトの問題を検出したりできます。

Chrome DevTools チームへのお問い合わせ

この投稿で紹介する新機能や変更点、またはその他の DevTools に関連する内容については、以下のオプションを使って議論してください。

  • ご提案やフィードバックは、crbug.com からお送りください。
  • DevTools の問題を報告するには、その他のオプションもっと見る) >ヘルプ >DevTools で DevTools の問題を報告します。
  • @ChromeDevTools でツイートしてください。
  • DevTools の新機能に関する YouTube 動画または DevTools のヒントの YouTube 動画にコメントを残してください。