Implementowanie debugowania CSP i Zaufanych typów w Narzędziach deweloperskich w Chrome

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

W tym poście omawiamy wdrożenie obsługi narzędzi deweloperskich w celu debugowania problemów z standardem Content Security Policy (CSP) za pomocą karty Problemy, którą wprowadziliśmy niedawno.

Prace wdrożeniowe zostały przeprowadzone w ramach 2 stażów: 1. W pierwszej kolejności stworzyliśmy ogólny system zgłaszania i opracowaliśmy komunikaty o 3 problemach związanych z naruszeniem zasad CSP. 2. W drugim natomiast dodaliśmy problemy dotyczące zaufanego typu oraz kilka specjalistycznych funkcji Narzędzi deweloperskich do debugowania typów zaufanych typów.

Co to jest Content Security Policy?

Content Security Policy (CSP) umożliwia ograniczenie niektórych zachowań na stronie w celu zwiększenia bezpieczeństwa. Za pomocą CSP można na przykład zablokować używanie skryptów w tekście lub eval. Takie rozwiązanie zmniejsza ryzyko ataków typu cross-site scripting (XSS). Szczegółowe wprowadzenie do CSP znajdziesz tutaj.

Szczególnie nowym standardem CSP jest zasada Trusted Type(TT), która umożliwia dynamiczną analizę umożliwiającą systematyczne zapobieganie wielu atakom polegającym na wstrzyknięciu na strony internetowe. W tym celu technologia TT pomaga witrynie w egzekwowaniu kodu JavaScript i pozwala na przypisywanie do ujść DOM tylko niektórych typów elementów, takich jak innerHTML.

Witryna może aktywować zasadę bezpieczeństwa treści, umieszczając określony nagłówek HTTP. Na przykład nagłówek content-security-policy: require-trusted-types-for 'script'; trusted-types default aktywuje zasadę TT dla strony.

Każda zasada może działać w jednym z tych trybów:

  • tryb wymuszany – w którym każde naruszenie zasad kończy się błędem;
  • tryb tylko do raportowania – zgłasza komunikat o błędzie jako ostrzeżenie, ale nie powoduje awarii strony internetowej.

Wdrażanie problemów związanych z zasadami bezpieczeństwa treści na karcie Problemy.

Celem tych prac było usprawnienie debugowania problemów z CSP. Rozważając nowe problemy, zespół Narzędzi deweloperskich postępuje w ten sposób:

  1. Definiowanie historii użytkowników. W interfejsie Narzędzi deweloperskich znajdź zestaw historii użytkowników, który będzie zawierał opis sposobu, w jaki programista stron internetowych powinien zbadać problem.
  2. Implementacja na poziomie frontendu. Na podstawie historii użytkowników określ, które informacje są wymagane do zbadania problemu w interfejsie (np.powiązane żądanie, nazwa pliku cookie, wiersz w skrypcie lub pliku HTML itp.).
  3. Wykrywanie problemu. Wskaż miejsca w przeglądarce, w których można wykryć problem w Chrome, i wskaż miejsce, w którym zgłosisz problem, podając odpowiednie informacje z kroku 2.
  4. Zapisz i wyświetl problemy. Przechowuj problemy w odpowiednim miejscu i udostępniaj je w Narzędziach deweloperskich po otwarciu
  5. Projektowanie tekstu o problemach Wymyśl tekst, który pomoże deweloperowi zrozumieć problem, a co ważniejsze – pomoże go rozwiązać.

Krok 1. Definiowanie historii użytkowników na potrzeby problemów z CSP

Przed rozpoczęciem prac wdrożeniowych stworzyliśmy dokument projektowy zawierający historie użytkowników, aby lepiej zrozumieć, co musimy zrobić. Zanotowaliśmy na przykład taką historię:


Jako programista, który właśnie zdał sobie sprawę, że część mojej witryny jest zablokowana, chcę:- ...ustalić, czy CSP jest przyczyną blokowania elementów iframe / obrazów w mojej witrynie – ...dowiedzieć się, która dyrektywa CSP powoduje zablokowanie określonego zasobu – ...wiem, jak zmienić dostawcę CSP witryny, aby umożliwić wyświetlanie aktualnie zablokowanych zasobów / wykonywanie aktualnie zablokowanych zasobów js.


Aby zapoznać się z tą historią użytkownika, stworzyliśmy kilka prostych przykładowych stron internetowych, które naruszały nasze zasady CSP, oraz przeanalizowaliśmy przykładowe strony, aby samemu zapoznać się z tym procesem. Oto kilka przykładowych stron (otwórz wersję demonstracyjną w otwartej karcie Problemy):

Dzięki temu dowiedzieliśmy się, że lokalizacja źródłowa była najważniejszym elementem informacji do debugowania problemów z CSP. Przydatne okazało się też szybkie znalezienie powiązanego elementu iframe i żądania w przypadku zablokowania zasobu oraz przydatny może być też bezpośredni link do elementu HTML w panelu Elementy w Narzędziach deweloperskich.

Krok 2. Wdrożenie w interfejsie

Na podstawie tych danych opracowaliśmy pierwszą wersję roboczą informacji, które chcemy udostępnić w Narzędziach deweloperskich za pomocą protokołu Chrome DevTools Protocol (CDP):

Poniżej znajduje się fragment pliku 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

Powyższa definicja zasadniczo koduje strukturę danych JSON. Jest napisany w prostym języku o nazwie PDL (język danych protokołów). Kod PDL jest używany w 2 celach. Najpierw wykorzystujemy PDL do generowania definicji TypeScriptu, z których korzysta interfejs frontendu Narzędzi dla programistów. Na przykład powyższa definicja PDL generuje następujący interfejs 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;
}

Po drugie, prawdopodobnie jeszcze ważniejsze, generujemy bibliotekę C++ na podstawie definicji, która generuje i wysyła takie struktury danych z backendu C++ Chromium do interfejsu Narzędzi dla programistów. Korzystając z tej biblioteki, można utworzyć obiekt ContentSecurityPolicyIssueDetails, korzystając z następującego fragmentu kodu C++:

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

Po ustaleniu, które informacje chcemy udostępnić, musieliśmy sprawdzić, skąd można je pobrać z Chromium.

Krok 3. Wykrywanie problemu

Aby udostępnić informacje protokołowi Chrome DevTools (CDP) w formacie opisanym w ostatniej sekcji, musieliśmy znaleźć w backendzie miejsce, w którym faktycznie są one dostępne. Na szczęście kod CSP miał już wąskie gardło w trybie „Tylko raporty”, dzięki czemu mogliśmy się dowiedzieć: ContentSecurityPolicy::ReportViolation zgłasza problemy z (opcjonalnym) punktem końcowym raportowania, który można skonfigurować w nagłówku HTTP CSP. Większość informacji, które chcieliśmy uwzględnić, była już dostępna, więc żadne duże zmiany w zapleczu nie były konieczne do prawidłowego funkcjonowania naszych narzędzi.

Krok 4. Zapisz i wyświetl problemy

Chcieliśmy też zgłosić problemy, które wystąpiły przed otwarciem Narzędzi deweloperskich – podobnie jak w przypadku komunikatów konsoli. Oznacza to, że nie zgłaszamy problemów od razu w interfejsie, ale korzystamy z miejsca na dane, na którym występują problemy niezależnie od tego, czy Narzędzia deweloperskie są otwarte. Po otwarciu Narzędzi deweloperskich (lub podłączeniu dowolnego innego klienta CDP) można ponownie odtworzyć wszystkie zarejestrowane wcześniej problemy.

To już koniec pracy nad backendem. Teraz musimy się skupić na tym, jak wskazać ten problem w interfejsie.

Krok 5. Opracuj tekst problemu

Opracowanie tekstu zgłoszenia problemów to proces, w którym oprócz naszego zajmuje się na przykład kilka zespołów. Na przykład często polegamy na informacjach od zespołu, który wdraża określoną funkcję (w tym przypadku był to zespół CSP) i oczywiście zespołu DevRel, który opracowuje rozwiązania dla programistów stron internetowych. Tekst problemu zwykle jest doprecyzowany, dopóki nie zostanie ukończony.

Zwykle zespół Narzędzi deweloperskich zaczyna od ogólnej wersji roboczej projektu:


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

Po iteracji uzyskaliśmy następujące wyniki:

ALT_TEXT_HERE

Jak widać, zaangażowanie zespołu ds. funkcji i DevRel sprawia, że opis jest o wiele bardziej jasny i precyzyjny.

Problemy z CSP na stronie możesz też znaleźć na karcie poświęconej naruszeniom CSP.

Debugowanie problemów dotyczących zaufanych typów

Bez odpowiednich narzędzi dla programistów praca z użyciem technologii TT na dużą skalę może być trudna.

Ulepszone drukowanie w konsoli

Podczas pracy z zaufanymi obiektami chcemy wyświetlać co najmniej taką samą ilość informacji, jak w przypadku niezaufanego komponentu. Niestety obecnie podczas wyświetlania zaufanego obiektu nie są wyświetlane informacje o opakowanym obiekcie.

Dzieje się tak, ponieważ wartość wyświetlana w konsoli jest domyślnie pobierana z wywołania .valueOf() w obiekcie. Jednak w przypadku typu Zaufany typ zwracana wartość nie jest zbyt przydatna. Zależy nam na tym, aby nazwa była podobna do tej, którą otrzymasz, gdy będziesz dzwonić pod numer .toString(). Aby to osiągnąć, musimy zmodyfikować V8 i Blink, aby wprowadzić specjalną obsługę obiektów typu zaufanego.

Chociaż z powodów historycznych, niestandardowa obsługa klienta została przeprowadzona w wersji 8, takie podejście ma poważne wady. Jest wiele obiektów, które wymagają wyświetlania niestandardowego, ale ich typ jest taki sam na poziomie JavaScriptu. Ponieważ V8 to zwykły kod JS, nie można w nim rozróżnić pojęć związanych z internetowym interfejsem API, takich jak Trusted Type. Z tego powodu V8 musi poprosić o pomoc swój umieszczony na stronie (Blink) o pomoc w ich rozróżnianiu.

W związku z tym przeniesienie tej części kodu do usługi Blink lub jakiegokolwiek elementu umieszczonego na stronie wygląda na logiczny wybór. Poza tym problemem jest wiele innych korzyści:

  • Każdy element umieszczany na stronie może mieć własne generowanie tekstu reklamy
  • Wygenerowanie opisu za pomocą interfejsu Blink API jest znacznie łatwiejsze
  • Blink ma dostęp do pierwotnej definicji obiektu. Jeśli do wygenerowania opisu użyjemy atrybutu .toString(), nie ma ryzyka, że zostanie ponownie zdefiniowana właściwość .toString().

Naruszenie zasad (w trybie „tylko zgłaszanie”)

Obecnie jedynym sposobem debugowania naruszeń zasad TT jest ustawienie punktów przerwania w wyjątkach JS. Wymuszane naruszenia zasad TT będą wyzwolić wyjątek, więc ta funkcja może być przydatna. Jednak w rzeczywistych scenariuszach potrzebujesz bardziej szczegółowej kontroli nad naruszeniami zasad dotyczących TT. Chcemy w szczególności dotyczy to tylko naruszeń zasad TT (a nie innych wyjątków), przerwy także w trybie „Tylko raport” i rozróżnienia między poszczególnymi typami naruszeń zasad.

Narzędzia deweloperskie obsługują już wiele różnych punktów przerwania, więc architektura jest dość elastyczna. Dodanie nowego typu punktu przerwania wymaga zmian w backendzie (Blink), CDP i frontendzie. Powinniśmy wprowadzić nowe polecenie CDP. Nazwij je setBreakOnTTViolation. Interfejs ten będzie używany przez frontend do informowania backendu, jakiego rodzaju naruszenia zasad przetwarzania tekstu powinny zostać uszkodzone. Backend, w szczególności InspectorDOMDebuggerAgent, udostępni „sondę” onTTViolation(), która będzie wywoływana za każdym razem, gdy wystąpi naruszenie zasad TT. Następnie InspectorDOMDebuggerAgent sprawdzi, czy to naruszenie zasad powinno aktywować punkt przerwania. Jeśli w takim przypadku zostanie wysłane, do frontendu zostanie wysłana wiadomość z prośbą o wstrzymanie wykonania.

Co się stało, a co dalej?

Od momentu wprowadzenia opisanych tu problemów na karcie Problemy wprowadzono kilka zmian:

Planujemy w przyszłości korzystać z karty Problemy, aby pokazywać więcej problemów, co pozwoli w dłuższej perspektywie na usunięcie nieczytelnego przepływu komunikatów o błędzie w konsoli.

Pobieranie kanałów podglądu

Jako domyślnej przeglądarki dla programistów możesz używać Chrome Canary, Dev lub Beta. Te kanały podglądu dają dostęp do najnowszych funkcji Narzędzi deweloperskich, umożliwiają testowanie najnowocześniejszych interfejsów API platform internetowych oraz wykrywanie problemów w witrynie, zanim zdołają zrobić użytkownicy.

Kontakt z zespołem Narzędzi deweloperskich w Chrome

Użyj poniższych opcji, aby omówić nowe funkcje i zmiany w poście lub wszelkie inne kwestie związane z Narzędziami dla deweloperów.

  • Prześlij nam sugestię lub opinię na stronie crbug.com.
  • Aby zgłosić problem z Narzędziami deweloperskimi, kliknij Więcej opcji   Więcej   > Pomoc > Zgłoś problemy z Narzędziami deweloperskimi.
  • zatweetuj na @ChromeDevTools.
  • Napisz komentarz o nowościach w filmach w YouTube dostępnych w Narzędziach deweloperskich lub z poradami dotyczącymi narzędzi dla deweloperów w filmach w YouTube.