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

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

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

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

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

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

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

ウェブサイトでは、特定の 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 の問題に関するユーザー ストーリーを定義する

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


ウェブサイトの一部がブロックされていることに気づいたデベロッパーとして、次の作業を行いたい:


このユーザー ストーリーを探索するために、関心のある CSP 違反を示す簡単なサンプル ウェブページをいくつか作成し、サンプルページを確認してプロセスに慣れるようにしました。 ウェブページの例をいくつか示します([問題] タブを開いてデモを開いてください)。

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

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

この分析情報を、Chrome DevTools プロトコル(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 プロトコル(CDP)で情報を利用できるようにするには、バックエンドで実際に情報を利用できる場所を見つける必要がありました。幸いなことに、CSP コードにはすでにレポート専用モードで使用されるボトルネックがあり、そこで ContentSecurityPolicy::ReportViolation を使用すると、CSP HTTP ヘッダーで構成できる(オプションの)レポート エンドポイントに問題を報告できます。報告したい情報のほとんどはすでに入手可能だったため、計測が機能するためにバックエンドに大きな変更を加える必要はありませんでした。

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

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

これでバックエンドの作業は完了となり、今度はフロントエンドで問題を特定する方法に集中する必要がありました。

ステップ 5: 問題文の設計

問題文の設計は、Google チームだけでなく複数のチームが関与するプロセスです。たとえば、機能を実装するチーム(この場合は CSP チーム)と、もちろん DevRel チームからの分析情報に依存します。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 Type の場合は、返される値はあまり役に立ちません。代わりに、.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、フロントエンドを変更する必要があります。新しい CDP コマンドを導入します。コマンドの名前は setBreakOnTTViolation とします。このコマンドはフロントエンドが使用し、中断するべき TT 違反の種類をバックエンドに伝えます。バックエンド(特に InspectorDOMDebuggerAgent)は、TT 違反が発生するたびに呼び出される「プローブ」onTTViolation() を提供します。次に、InspectorDOMDebuggerAgent は、その違反によってブレークポイントがトリガーされるかどうかを確認します。その場合、フロントエンドにメッセージを送信して実行を一時停止します。

現在の作業と今後の予定

ここで説明する問題の発生に伴い、[問題] タブに大きな変更が加えられています。

今後は、[問題] タブを使用してより多くの問題を明らかにし、長期的には読み取り不能なエラー メッセージ フローのコンソールをアンロードできるようにする予定です。

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

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

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

投稿内の新機能や変更点、または DevTools に関するその他のことについて話し合うには、次のオプションを使用します。

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