在 Chrome 開發人員工具中實作 CSP 和 Trusted Types 偵錯功能

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

這篇網誌文章說明如何實作開發人員工具支援,並運用最近推出的問題分頁,對內容安全政策 (CSP) 問題進行偵錯。

導入作業是在 2 次實習期間完成: 1. 在第一階段,我們建構了一般回報架構,並針對 3 項 CSP 違規問題設計問題訊息。2. 在第二個階段,我們新增了 Trusted Type 問題,以及一些專門用於 Trusted Type 偵錯的開發人員工具功能。

什麼是內容安全政策?

內容安全政策 (CSP) 可限制網站中的特定行為,提高安全性。舉例來說,CSP 可用於禁止內嵌指令碼或禁止 eval,這兩者都能減少跨網站指令碼攻擊 (XSS) 的攻擊面。如需 CSP 的詳細介紹,請參閱這裡。

可信任類型(TT) 政策是相當新穎的 CSP,可啟用動態分析,系統性地防止網站遭受大量注入式攻擊。為此,TT 支援網站監控 JavaScript 程式碼,只允許將特定類型的內容指派給 DOM 接收器,例如 innerHTML。

網站可以加入特定 HTTP 標頭,啟用內容安全政策。例如,標頭 content-security-policy: require-trusted-types-for 'script'; trusted-types default 會啟用網頁的 TT 政策。

每項政策都能以其中一種模式運作:

  • 強制執行模式:所有違反政策的行為都會視為錯誤。
  • 僅限回報模式:將錯誤訊息回報為警告,但不會導致網頁失敗。

「問題」分頁中的「實作內容安全政策」問題

這項工作的目標是改善 CSP 問題的偵錯體驗。DevTools 團隊在考量新問題時,大致會遵循以下流程:

  1. 定義使用者故事。在開發人員工具前端找出使用者故事,涵蓋網頁程式開發人員調查問題時需要採取的步驟。
  2. 前端實作。根據使用者故事,找出調查前端問題所需的資訊 (例如相關要求、Cookie 名稱、指令碼或 HTML 檔案中的一行等)。
  3. 問題偵測。找出 Chrome 瀏覽器中可偵測到問題的位置,並檢測該位置以回報問題,包括步驟 (2) 中的相關資訊。
  4. 儲存並顯示問題。將問題儲存在適當位置,並在開發人員工具開啟時提供
  5. 設計問題文字。撰寫說明文字,協助網頁程式開發人員瞭解問題,更重要的是修正問題

步驟 1:定義 CSP 問題的使用者故事

在開始實作之前,我們建立了設計文件,其中包含使用者故事,以便進一步瞭解需要執行的操作。舉例來說,我們寫下以下使用者故事:


身為開發人員,我剛發現網站有部分內容遭到封鎖,因此想: - ...瞭解 CSP 是否為網站 iframe / 圖片遭到封鎖的原因 - ...瞭解哪項 CSP 指令導致特定資源遭到封鎖 - ...瞭解如何變更網站的 CSP,允許顯示目前遭到封鎖的資源 / 執行目前遭到封鎖的 JS。


為了探索這個使用者故事,我們建立了一些簡單的範例網頁,展示我們感興趣的 CSP 違規行為,並探索範例網頁,熟悉流程。以下是部分範例網頁 (開啟示範網頁時,請一併開啟「問題」分頁):

透過這個程序,我們發現來源位置是偵錯 CSP 問題最重要的資訊。我們也發現,在資源遭到封鎖時,快速找出相關聯的 iframe 和要求,以及在開發人員工具的「Elements」面板中直接連結至 HTML 元素,也很有幫助。

步驟 2:前端導入

我們將這項洞察結果轉化為資訊初稿,並希望透過 Chrome 開發人員工具通訊協定 (CDP) 提供給開發人員工具:

以下是 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 有兩種用途。首先,我們使用 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++ 程式庫,負責從 C++ Chromium 後端產生並傳送這些資料結構至開發人員工具前端。使用該程式庫時,您可以用下列 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 開發人員工具通訊協定 (CDP) 能夠以最後一節所述的格式取得資訊,我們必須找出資訊在後端的實際位置。幸好,CSP 程式碼已有用於僅限報表模式的瓶頸,我們可以在其中掛鉤:ContentSecurityPolicy::ReportViolation 會將問題回報給 (選用) 回報端點,該端點可在 CSP HTTP 標頭中設定。我們想回報的大部分資訊都已存在,因此不需要對後端進行重大變更,檢測功能就能運作。

步驟 4:儲存並顯示問題

不過,我們也想回報開發人員工具開啟前發生的問題,這點與控制台訊息的處理方式類似,因此會稍微複雜一些。也就是說,我們不會直接向前端回報問題,而是使用一個儲存空間,無論開發人員工具是否開啟,都會填入問題。開啟開發人員工具 (或連結任何其他 CDP 用戶端) 後,所有先前記錄的問題都可以從儲存空間重播。

後端工作到此結束,接下來我們需要著重於如何在前端顯示問題。

步驟 5:設計問題文字

設計問題文字的過程涉及多個團隊,除了我們自己的團隊外,我們也經常仰賴實作功能的團隊 (在本例中為 CSP 團隊) 和 DevRel 團隊的洞察,後者負責設計網頁程式開發人員應如何處理特定類型的問題。問題文字通常會經過一些修正,直到完成為止。

通常,開發人員工具團隊會先根據想像,繪製粗略的草稿:


## 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

如您所見,在功能團隊和開發人員關係團隊的協助下,說明變得更加清楚精確!

您也可以在專門用於 CSP 違規事項的分頁中,查看網頁上的 CSP 問題。

Trusted Types 問題偵錯

如果沒有合適的開發人員工具,大規模使用 TT 可能會相當困難。

改善控制台列印功能

處理信任物件時,我們希望顯示的資訊量至少與非信任物件相同。很遺憾,目前顯示信任物件時,不會顯示包裝物件的相關資訊。

這是因為控制台顯示的值,預設是從物件呼叫 .valueOf() 取得。不過,如果是 Trusted Type,傳回的值並不是很有用。我們希望取得類似呼叫 .toString() 時取得的內容。為此,我們需要修改 V8 和 Blink,為信任類型物件導入特殊處理機制。

雖然基於歷史原因,這項自訂處理是在 V8 中完成,但這種做法有重大缺點。許多物件需要自訂顯示,但在 JS 層級的類型相同。由於 V8 是純 JS,因此無法區分與 Web API (例如 Trusted Type) 對應的概念。因此,V8 必須向嵌入器 (Blink) 尋求協助,才能區分兩者。

因此,將這部分程式碼移至 Blink 或任何嵌入器,似乎是合理的選擇。除了發現問題外,還有許多其他好處:

  • 每個嵌入器都可以有自己的說明生成
  • 透過 Blink API 生成說明會更輕鬆
  • Blink 可存取物件的原始定義。因此,如果我們使用 .toString() 產生說明,就不會有 .toString() 重新定義的風險。

違規時中斷 (僅限報表模式)

目前,偵錯 TT 違規的唯一方法是在 JS 例外狀況上設定中斷點。由於強制執行的 TT 違規行為會觸發例外狀況,因此這項功能在某些情況下相當實用。不過,在實際情境中,您需要更精細地控管 TT 違規行為。具體來說,我們希望只在 TT 違規時中斷 (而非其他例外狀況),在僅限報告模式中也中斷,並區分不同類型的 TT 違規。

開發人員工具已支援多種中斷點,因此架構相當容易擴充。如要新增中斷點類型,必須變更後端 (Blink)、CDP 和前端。我們應該推出新的 CDP 指令,暫且稱為 setBreakOnTTViolation。前端會使用這個指令,告知後端應中斷哪些類型的 TT 違規行為。後端 (尤其是 InspectorDOMDebuggerAgent) 會提供「探測」onTTViolation(),每次發生 TT 違規時都會呼叫。接著,InspectorDOMDebuggerAgent 會檢查該違規行為是否應觸發中斷點,如果是,則會傳送訊息至前端,暫停執行。

已完成和待完成的工作

自本文所述問題推出以來,「Issues」分頁已歷經多次變更:

我們計畫日後透過「問題」分頁顯示更多問題,長期下來,主控台就不會再顯示難以解讀的錯誤訊息。

下載預覽管道

建議使用 Chrome Canary、開發人員版或 Beta 版做為預設開發瀏覽器。這些預覽管道可讓您存取最新的開發人員工具功能、測試尖端的網路平台 API,並在使用者發現網站問題前,搶先找出問題!

與 Chrome 開發人員工具團隊聯絡

您可以透過下列管道,討論新功能、更新或任何與開發人員工具相關的事項。