このブログ投稿では、最近導入された [問題] タブを使用して、コンテンツ セキュリティ ポリシー(CSP)の問題をデバッグするための DevTools サポートの実装について説明します。
実装作業は、次の 2 つのインターンシップで行われました。最初のフェーズでは、一般的な報告フレームワークを構築し、3 件の CSP 違反の問題に関する問題メッセージを設計しました。2. 2 回目のリリースでは、Trusted Types のデバッグ用に、Trusted Types の問題と専用の DevTools 機能を追加しました。
コンテンツ セキュリティ ポリシーとは
コンテンツ セキュリティ ポリシー(CSP)を使用すると、ウェブサイトでの特定の動作を制限してセキュリティを強化できます。たとえば、CSP を使用してインライン スクリプトを禁止したり、eval
を禁止したりできます。どちらも、クロスサイト スクリプティング(XSS)攻撃の攻撃対象領域を減らすことができます。CSP の詳細については、こちらをご覧ください。
特に新しい CSP は、信頼できる型(TT)ポリシーです。このポリシーにより、ウェブサイトに対するさまざまなインジェクション攻撃を体系的に防ぐ動的分析が可能になります。これを実現するため、TT はウェブサイトの JavaScript コードを監視し、innerHTML などの DOM シンクに特定のタイプのオブジェクトのみを割り当てられるようにします。
ウェブサイトで特定の HTTP ヘッダーを含めることで、コンテンツ セキュリティ ポリシーを有効にできます。たとえば、ヘッダー content-security-policy: require-trusted-types-for 'script'; trusted-types default
はページの TT ポリシーを有効にします。
各ポリシーは、次のいずれかのモードで動作できます。
- 自動適用モード - すべてのポリシー違反がエラーになります。
- 報告のみモード - エラー メッセージは警告として報告されますが、ウェブページの失敗は発生しません。
[問題] タブのコンテンツ セキュリティ ポリシーの実装に関する問題
この取り組みの目標は、CSP の問題に対するデバッグ エクスペリエンスを改善することでした。新しい問題を検討する際、DevTools チームは概ね次のプロセスに沿って対応します。
- ユーザー ストーリーの定義。DevTools フロントエンドで、ウェブ デベロッパーがどのように問題を調査する必要があるかを説明する一連のユーザー ストーリーを特定します。
- フロントエンドの実装。ユーザー ストーリーに基づいて、フロントエンドで問題の調査に必要な情報(関連するリクエスト、Cookie の名前、スクリプトまたは html ファイルの行など)を特定します。
- 問題の検出。Chrome で問題を検出できるブラウザ内の場所を特定し、手順(2)の関連情報を含む問題を報告するようにその場所を計測します。
- 問題を保存して表示する。問題を適切な場所に保存し、DevTools が開かれたときに利用できるようにする
- 問題のテキストの設計。ウェブ デベロッパーが問題を把握し、さらには修正する際に役立つ説明文を作成します。
ステップ 1: CSP の問題のユーザー ストーリーを定義する
実装作業を開始する前に、ユーザー ストーリーを含む設計ドキュメントを作成し、必要な作業を明確にしました。たとえば、次のようなユーザー ストーリーを記述しました。
ウェブサイトの一部がブロックされていることに気づいたデベロッパーとして:- - ...ウェブサイトで iframe / 画像がブロックされる原因が CSP にあるかどうかを調べる - ...どの CSP ディレクティブが特定のリソースのブロックを引き起こしているかを調べる - ...ウェブサイトの CSP を変更して、現在ブロックされているリソースの表示 / 現在ブロックされている js の実行を許可する方法を知る
このユーザー ストーリーを調査するため、興味のある CSP 違反を示す簡単なサンプル ウェブページをいくつか作成し、サンプルページを調べてプロセスに慣れました。ウェブページの例を次に示します([問題] タブを開いた状態でデモを開きます)。
このプロセスから、ソースの場所が CSP の問題をデバッグするうえで最も重要な情報であることがわかりました。また、リソースがブロックされた場合に関連する iframe とリクエストをすばやく見つけることも、DevTools の [要素] パネルの 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;
}
2 つ目は、より重要なことですが、これらのデータ構造の生成と 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::ReportViolation
は、CSP HTTP ヘッダーで構成できる(省略可)レポート エンドポイントに問題を報告します。報告する情報のほとんどはすでに利用可能だったため、計測を機能させるためにバックエンドで大きな変更を加える必要はありませんでした。
ステップ 4: 問題を保存して表示する
コンソール メッセージの処理と同様に、DevTools が開かれる前に発生した問題も報告できるようにしたいという要望もありました。つまり、問題をフロントエンドに直接報告するのではなく、DevTools が開いているかどうかに関係なく問題が入力されるストレージを使用します。DevTools が開かれた後(または、他の CDP クライアントが接続された後)、以前に記録された問題はすべてストレージから再生できます。
これでバックエンドの作業は完了しました。次は、フロントエンドで問題を表示する方法を検討します。
ステップ 5: 問題のテキストを設計する
問題のテキストの設計は、Google のチーム以外にも複数のチームが関与するプロセスです。たとえば、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/
繰り返し処理した結果、次のことが判明しました。
ご覧のとおり、機能チームと DevRel を巻き込むと、説明がより明確で正確になります。
ページに表示された CSP の問題は、CSP 違反専用のタブでも見つけることができます。
Trusted Type の問題のデバッグ
適切なデベロッパー ツールがなければ、大規模な TT の操作は困難になる可能性があります。
コンソール出力の改善
信頼できるオブジェクトを扱う場合は、信頼できないオブジェクトの場合と同等以上の情報を表示する必要があります。残念ながら、現在のところ、信頼できるオブジェクトを表示しても、ラップされたオブジェクトに関する情報は表示されません。
これは、コンソールに表示される値が、デフォルトでオブジェクトに対する .valueOf()
の呼び出しから取得されるためです。ただし、Trusted Types の場合、戻り値はあまり役に立ちません。代わりに、.toString()
を呼び出したときに得られるものに似たものを用意したいと考えています。これを実現するには、V8 と Blink を変更して、信頼できる型のオブジェクトに対する特別な処理を導入する必要があります。
歴史的な理由から、このカスタム処理は V8 で行われていましたが、このようなアプローチには重要な欠点があります。カスタム表示が必要なオブジェクトは多数ありますが、そのタイプは JS レベルでは同じです。V8 は純粋な JS であるため、信頼できる型などの Web 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
は、その違反でブレークポイントをトリガーする必要があるかどうかを確認し、トリガーする必要がある場合は、実行を一時停止するようフロントエンドにメッセージを送信します。
完了内容と次のステップ
ここで説明する問題が導入されてから、[問題] タブは大幅に変更されています。
- DevTools の他のパネルとの相互接続性が改善されました。
- その他の多くの問題の報告は、[問題] タブに移動されました。低コントラスト、信頼できるウェブ アクティビティ、クイックモード、アトリビューション レポート API、CORS 関連の問題などです。
- 問題を非表示にする機能が導入されました
今後は、[問題] タブを使用してより多くの問題を表示する予定です。これにより、読み取り不能なエラー メッセージのフローをコンソールから削除できるようになります。
プレビュー チャンネルをダウンロードする
デフォルトの開発用ブラウザとして Chrome の Canary、Dev、Beta を使用することを検討してください。これらのプレビュー チャンネルでは、最新の DevTools 機能にアクセスしたり、最先端のウェブ プラットフォーム API をテストしたりできます。また、ユーザーよりも早くサイトの問題を見つけることもできます。
Chrome DevTools チームに問い合わせる
以下のオプションを使用して、新機能、アップデート、DevTools に関連するその他の内容について話し合います。
- フィードバックや機能リクエストは crbug.com から送信してください。
- DevTools で [その他] > [ヘルプ] > [DevTools の問題を報告] を使用して、DevTools の問題を報告します。
- @ChromeDevTools にツイートします。
- DevTools の新機能に関する YouTube 動画または DevTools のヒントに関する YouTube 動画にコメントを残してください。