Triển khai quy trình gỡ lỗi CSP và loại đáng tin cậy trong Công cụ của Chrome cho nhà phát triển

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

Bài đăng trên blog này trình bày về việc triển khai tính năng hỗ trợ của Công cụ dành cho nhà phát triển để gỡ lỗi các vấn đề về Chính sách bảo mật nội dung (CSP) nhờ thẻ Vấn đề mới ra mắt gần đây.

Công việc triển khai được thực hiện trong 2 cơ hội thực tập: 1. Trong giai đoạn đầu tiên, chúng tôi đã xây dựng khung báo cáo chung và thiết kế thông báo về vấn đề cho 3 vấn đề vi phạm CSP. 2. Trong lần phát hành thứ hai, chúng tôi đã thêm các vấn đề về Loại tin cậy cùng với một số tính năng chuyên biệt của DevTools để gỡ lỗi Loại tin cậy.

Chính sách bảo mật nội dung là gì?

Chính sách bảo mật nội dung (CSP) cho phép hạn chế một số hành vi nhất định trong trang web để tăng cường bảo mật. Ví dụ: bạn có thể dùng CSP để chặn các tập lệnh cùng dòng hoặc không cho phép eval. Cả hai cách này đều làm giảm bề mặt tấn công cho các cuộc tấn công bằng cách tấn công Cross-Site Scripting (XSS). Để biết thông tin giới thiệu chi tiết về CSP, hãy đọc tại đây.

Một CSP đặc biệt mới là chính sách Loại đáng tin cậy(TT). Chính sách này cho phép phân tích động có thể ngăn chặn một cách có hệ thống một loạt các cuộc tấn công chèn trên trang web. Để đạt được điều này, TT hỗ trợ một trang web trong việc kiểm tra mã JavaScript để chỉ cho phép một số loại nội dung nhất định được chỉ định cho các bồn lưu trữ DOM như innerHTML.

Một trang web có thể kích hoạt chính sách bảo mật nội dung bằng cách thêm một tiêu đề HTTP cụ thể. Ví dụ: tiêu đề content-security-policy: require-trusted-types-for 'script'; trusted-types default sẽ kích hoạt chính sách TT cho một trang.

Mỗi chính sách có thể hoạt động ở một trong các chế độ sau:

  • chế độ thực thi – trong đó mọi lỗi vi phạm chính sách đều là lỗi,
  • chế độ chỉ báo cáo – chế độ này báo cáo thông báo lỗi dưới dạng cảnh báo nhưng không gây ra lỗi trên trang web.

Triển khai các vấn đề về Chính sách bảo mật nội dung trong thẻ Vấn đề

Mục tiêu của công việc này là cải thiện trải nghiệm gỡ lỗi cho các vấn đề về CSP. Khi xem xét các vấn đề mới, nhóm Công cụ cho nhà phát triển thường tuân theo quy trình này:

  1. Xác định câu chuyện của người dùng. Xác định một bộ câu chuyện người dùng trong giao diện người dùng của DevTools, trong đó trình bày cách một nhà phát triển web cần điều tra vấn đề.
  2. Triển khai giao diện người dùng. Dựa trên câu chuyện của người dùng, hãy xác định những thông tin cần thiết để điều tra vấn đề ở phần giao diện người dùng (ví dụ: một yêu cầu có liên quan, tên của một cookie, một dòng trong tập lệnh hoặc tệp html, v.v.).
  3. Phát hiện vấn đề. Xác định những vị trí trong trình duyệt có thể phát hiện vấn đề trong Chrome và đo lường vị trí đó để báo cáo vấn đề, bao gồm cả thông tin liên quan từ bước (2).
  4. Lưu và hiển thị các vấn đề. Lưu trữ các vấn đề ở một vị trí thích hợp và cung cấp cho DevTools sau khi mở
  5. Thiết kế văn bản về vấn đề. Hãy nghĩ ra nội dung giải thích giúp nhà phát triển web hiểu và quan trọng hơn là có thể khắc phục vấn đề

Bước 1: Xác định câu chuyện người dùng cho các vấn đề về CSP

Trước khi bắt đầu công việc triển khai, chúng tôi đã tạo một tài liệu thiết kế có các câu chuyện người dùng để hiểu rõ hơn những việc cần làm. Ví dụ: chúng tôi đã viết ra câu chuyện người dùng sau:


Là một nhà phát triển, vừa nhận ra rằng một số phần trên trang web của mình bị chặn, tôi muốn:- - ...tìm hiểu xem CSP có phải là nguyên nhân khiến iframe / hình ảnh bị chặn trên trang web của mình không - ...tìm hiểu lệnh CSP nào gây ra sự chặn một tài nguyên nhất định - ...biết cách thay đổi CSP của trang web để cho phép hiển thị các tài nguyên đang bị chặn / thực thi của js hiện đang bị chặn.


Để khám phá câu chuyện người dùng này, chúng tôi đã tạo một số trang web mẫu đơn giản cho thấy các lỗi vi phạm CSP mà chúng tôi quan tâm, đồng thời khám phá các trang mẫu để tự làm quen với quy trình này. Sau đây là một số trang web mẫu (mở bản minh hoạ với thẻ Vấn đề đang mở):

Khi sử dụng quy trình này, chúng tôi nhận thấy vị trí nguồn là thông tin quan trọng nhất để gỡ lỗi các vấn đề về CSP. Chúng tôi cũng nhận thấy việc nhanh chóng tìm thấy iframe và yêu cầu liên quan trong trường hợp tài nguyên bị chặn cũng rất hữu ích, đồng thời đường liên kết trực tiếp đến phần tử HTML trong bảng điều khiển Elements (Thành phần) của DevTools cũng có thể hữu ích.

Bước 2: Triển khai phần giao diện người dùng

Chúng tôi đã chuyển thông tin chi tiết này thành bản nháp đầu tiên của thông tin mà chúng tôi muốn cung cấp cho DevTools thông qua Giao thức Chrome DevTools (CDP):

Dưới đây là trích dẫn từ 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

Về cơ bản, định nghĩa ở trên mã hoá một cấu trúc dữ liệu JSON. Tệp này được viết bằng một ngôn ngữ đơn giản có tên là PDL (ngôn ngữ dữ liệu giao thức). PDL được dùng cho hai mục đích. Trước tiên, chúng ta sử dụng PDL để tạo các định nghĩa TypeScript mà giao diện người dùng DevTools dựa vào. Ví dụ: định nghĩa PDL ở trên sẽ tạo ra giao diện TypeScript sau:

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;
}

Thứ hai, và có lẽ quan trọng hơn, chúng ta tạo một thư viện C++ từ định nghĩa xử lý việc tạo và gửi các cấu trúc dữ liệu này từ phần phụ trợ Chromium C++ đến phần giao diện người dùng DevTools. Khi sử dụng thư viện đó, bạn có thể tạo đối tượng ContentSecurityPolicyIssueDetails bằng đoạn mã C++ sau:

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

Sau khi đã xác định được thông tin mà chúng tôi muốn cung cấp, chúng tôi cần khám phá nơi có thể lấy thông tin này từ Chromium.

Bước 3: Phát hiện vấn đề

Để cung cấp thông tin cho Giao thức công cụ phát triển Chrome (CDP) ở định dạng được mô tả trong phần cuối, chúng ta cần tìm vị trí thực sự có thông tin trong phần phụ trợ. May mắn là mã CSP đã gây ra nút thắt cổ chai dùng cho chế độ chỉ báo cáo, ở đó chúng ta có thể kết nối vào: ContentSecurityPolicy::ReportViolation báo cáo vấn đề tới một điểm cuối báo cáo (không bắt buộc) có thể được định cấu hình trong tiêu đề HTTP CSP. Hầu hết thông tin chúng tôi muốn báo cáo đã có sẵn, vì vậy, các chức năng đo lường của chúng tôi không cần thực hiện thay đổi lớn nào trong phần phụ trợ.

Bước 4: lưu và hiển thị các vấn đề

Một chức năng nhỏ là việc chúng ta cũng muốn báo cáo các vấn đề xảy ra trước khi Công cụ cho nhà phát triển được mở, tương tự như cách xử lý thông báo trên bảng điều khiển. Điều này có nghĩa là chúng ta không báo cáo vấn đề ngay lập tức cho phần giao diện người dùng, mà sử dụng một bộ nhớ được điền sẵn các vấn đề, bất kể DevTools có mở hay không. Sau khi mở DevTools (hoặc bất kỳ ứng dụng khách CDP nào khác được đính kèm), bạn có thể phát lại tất cả các vấn đề đã ghi trước đó từ bộ nhớ.

Điều này đã kết thúc công việc phụ trợ và bây giờ, chúng tôi cần tập trung vào cách hiển thị vấn đề trong giao diện người dùng.

Bước 5: thiết kế văn bản về vấn đề

Việc thiết kế văn bản về vấn đề là một quy trình liên quan đến một số nhóm ngoài nhóm của chúng tôi, ví dụ: chúng tôi thường dựa vào thông tin chi tiết của nhóm triển khai một tính năng (trong trường hợp này là nhóm CSP) và tất nhiên là nhóm DevRel, nhóm này thiết kế cách nhà phát triển web xử lý một loại vấn đề nhất định. Thông tin về vấn đề thường được tinh chỉnh lại cho đến khi hoàn tất.

Thường thì nhóm DevTools sẽ bắt đầu bằng một bản phác thảo sơ lược về những gì họ tưởng tượng:


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

Sau khi lặp lại, chúng ta đã có:

ALT_TEXT_HERE

Như bạn có thể thấy, việc tham gia của nhóm tính năng và DevRel giúp nội dung mô tả trở nên rõ ràng và chính xác hơn rất nhiều!

Bạn cũng có thể tìm hiểu các vấn đề về CSP trên trang của mình trong thẻ dành riêng cho các trường hợp vi phạm CSP.

Khắc phục sự cố về Loại đáng tin cậy

Việc làm việc với TT ở quy mô lớn có thể gặp khó khăn nếu không có các công cụ phù hợp dành cho nhà phát triển.

Cải thiện tính năng in trên bảng điều khiển

Khi làm việc với Đối tượng đáng tin cậy, chúng ta muốn hiển thị lượng thông tin ít nhất bằng với đối tượng không đáng tin cậy. Rất tiếc, hiện tại khi hiển thị Đối tượng đáng tin cậy, sẽ không có thông tin nào về đối tượng được gói.

Đó là do giá trị hiển thị trong bảng điều khiển được lấy từ lệnh gọi .valueOf() trên đối tượng theo mặc định. Tuy nhiên, trong trường hợp Loại đáng tin cậy, giá trị được trả về không hữu ích lắm. Thay vào đó, chúng ta muốn có dữ liệu tương tự như những gì bạn nhận được khi gọi .toString(). Để đạt được điều này, chúng ta cần sửa đổi V8 và Blink để đưa ra phương thức xử lý đặc biệt cho các đối tượng loại đáng tin cậy.

Mặc dù do lý do lịch sử, việc xử lý tuỳ chỉnh này được thực hiện trong V8, nhưng phương pháp này có những nhược điểm quan trọng. Có nhiều đối tượng yêu cầu hiển thị tuỳ chỉnh nhưng loại của các đối tượng này giống nhau ở cấp JS. Vì V8 là JS thuần tuý, nên không thể phân biệt các khái niệm tương ứng với API Web như Loại đáng tin cậy. Vì lý do đó, V8 phải yêu cầu trình nhúng (Blink) trợ giúp để phân biệt các thành phần này.

Do đó, việc di chuyển phần mã đó sang Blink hoặc bất kỳ trình nhúng nào có vẻ là một lựa chọn hợp lý. Ngoài vấn đề đã nêu, còn có nhiều lợi ích khác:

  • Mỗi trình nhúng có thể có phương thức tạo nội dung mô tả riêng
  • Bạn có thể tạo nội dung mô tả dễ dàng hơn thông qua API Blink
  • Blink có quyền truy cập vào định nghĩa ban đầu của đối tượng. Do đó, nếu chúng ta sử dụng .toString() để tạo nội dung mô tả, thì .toString() sẽ không có nguy cơ bị xác định lại.

Đột phá khi vi phạm (ở chế độ chỉ báo cáo)

Hiện tại, cách duy nhất để gỡ lỗi các lỗi vi phạm TT là đặt điểm ngắt trên các ngoại lệ JS. Vì các lỗi vi phạm TT được thực thi sẽ kích hoạt một ngoại lệ, nên tính năng này có thể hữu ích theo một cách nào đó. Tuy nhiên, trong các trường hợp thực tế, bạn cần có quyền kiểm soát chi tiết hơn đối với các lỗi vi phạm TT. Cụ thể, chúng tôi muốn chỉ phân biệt ở chế độ vi phạm TT (không phải các trường hợp ngoại lệ khác), cũng ở chế độ chỉ báo cáo và phân biệt các loại vi phạm TT.

DevTools đã hỗ trợ nhiều điểm ngắt nên kiến trúc này khá dễ mở rộng. Việc thêm một loại điểm ngắt mới đòi hỏi phải thay đổi phần phụ trợ (Blink), CDP và giao diện người dùng. Chúng ta nên giới thiệu một lệnh CDP mới, hãy gọi lệnh này là setBreakOnTTViolation. Lệnh này sẽ được giao diện người dùng sử dụng để cho phần phụ trợ biết loại lỗi vi phạm TT mà nó sẽ phá vỡ. Phần phụ trợ, cụ thể là InspectorDOMDebuggerAgent, sẽ cung cấp một "thăm dò", onTTViolation() sẽ được gọi mỗi khi xảy ra lỗi vi phạm TT. Sau đó, InspectorDOMDebuggerAgent sẽ kiểm tra xem lỗi vi phạm đó có kích hoạt điểm ngắt hay không. Nếu có, InspectorDOMDebuggerAgent sẽ gửi thông báo đến giao diện người dùng để tạm dừng quá trình thực thi.

Những việc đã làm và việc cần làm tiếp theo

Kể từ khi các vấn đề được mô tả ở đây được giới thiệu, thẻ Vấn đề đã trải qua khá nhiều thay đổi:

Từ giờ trở đi, chúng tôi dự định sử dụng thẻ Vấn đề để tìm ra nhiều vấn đề hơn, giúp bạn có thể huỷ tải luồng thông báo lỗi không đọc được trên Play Console về lâu dài.

Tải các kênh xem trước xuống

Hãy cân nhắc sử dụng Chrome Canary, Dev hoặc Beta làm trình duyệt phát triển mặc định. Các kênh xem trước này cho phép bạn sử dụng các tính năng mới nhất của DevTools, kiểm thử các API nền tảng web tiên tiến và giúp bạn tìm thấy vấn đề trên trang web của mình trước khi người dùng phát hiện ra!

Liên hệ với nhóm Công cụ của Chrome cho nhà phát triển

Hãy sử dụng các lựa chọn sau để thảo luận về các tính năng, bản cập nhật mới hoặc bất kỳ nội dung nào khác liên quan đến Công cụ cho nhà phát triển.