このブログ投稿では、最近導入された [問題] タブを使用して、コンテンツ セキュリティ ポリシー(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 ディレクティブを学びます - ...現在ブロックされているリソースの表示や、現在ブロックされている js の実行を許可するようにウェブサイトの CSP を変更する方法を学びます。
このユーザー ストーリーを調査するため、興味のある 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()
の呼び出しから取得されるためです。ただし、信頼できるタイプの場合、返される値はあまり有用ではありません。代わりに、.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 の他のパネルとの相互接続性が改善されました。
- その他の多くの問題の報告は、[問題] タブに移動されました。低コントラスト、信頼できるウェブ アクティビティ、クイックモード、Attribution Reporting API、CORS 関連の問題などです。
- 問題を非表示にする機能が導入されました
今後は、[問題] タブを使用して、より多くの問題を表示する予定です。これにより、読み取り不能なエラー メッセージのフローをコンソールから削除できるようになります。
プレビュー チャネルをダウンロードする
デフォルトの開発用ブラウザとして Chrome の Canary、Dev、Beta を使用することを検討してください。これらのプレビュー チャンネルでは、最新の DevTools 機能にアクセスしたり、最先端のウェブ プラットフォーム API をテストしたりできます。また、ユーザーよりも早くサイトの問題を見つけることもできます。
Chrome DevTools チームに問い合わせる
次のオプションを使用して、DevTools の新機能、アップデート、その他のトピックについて話し合います。
- フィードバックや機能リクエストは crbug.com から送信してください。
- DevTools で [その他] > [ヘルプ] > [DevTools の問題を報告] を使用して、DevTools の問題を報告します。
- @ChromeDevTools にツイートします。
- DevTools の新機能に関する YouTube 動画または DevTools のヒントに関する YouTube 動画にコメントを残してください。