Como implementar a CSP e depurar os Tipos confiáveis no Chrome DevTools

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

Esta postagem do blog é sobre a implementação do suporte do DevTools para depuração de problemas da Política de Segurança de Conteúdo (CSP) com a ajuda da recentemente lançada a guia Problemas.

O trabalho de implementação foi feito em dois estágios: 1. No primeiro, criamos o framework geral de denúncias e criamos as mensagens para três problemas de violação da CSP. 2. Durante o segundo, adicionamos problemas de Tipo confiável com alguns recursos especializados do DevTools para depuração dos Tipos confiáveis.

O que é uma Política de Segurança de Conteúdo?

A Política de Segurança de Conteúdo (CSP) permite restringir determinados comportamentos em um site para aumentar a segurança. Por exemplo, a CSP pode ser usada para proibir scripts in-line ou eval, o que reduz a superfície de ataque de ataques de Scripting em vários locais (XSS). Para uma introdução detalhada à CSP, clique aqui.

Uma CSP particularmente nova é a política de Tipos confiáveis(TT, na sigla em inglês), que permite uma análise dinâmica que pode impedir sistematicamente uma grande classe de ataques de injeção nos sites. Para conseguir isso, o TT oferece suporte a um site na administração do código JavaScript para permitir que apenas certos tipos de itens sejam atribuídos a coletores DOM, como innerHTML.

Um site pode ativar uma Política de Segurança de Conteúdo incluindo um cabeçalho HTTP específico. Por exemplo, o cabeçalho content-security-policy: require-trusted-types-for 'script'; trusted-types default ativa a política de TT de uma página.

Cada política pode operar em um destes modos:

  • modo aplicado: em que toda violação da política é um erro,
  • modo somente de relatório: informa a mensagem de erro como um aviso, mas não causa falha na página da Web.

Como implementar a Política de Segurança de Conteúdo na guia Problemas

O objetivo desse trabalho era melhorar a experiência de depuração de problemas de CSP. Ao considerar novos problemas, a equipe do DevTools segue este processo:

  1. Como definir histórias de usuário. Identifique um conjunto de histórias de usuários no front-end do DevTools que abrange como um desenvolvedor da Web precisaria investigar o problema.
  2. Implementação de front-end: Com base nas histórias de usuários, identifique quais informações são necessárias para a investigação do problema no front-end (por exemplo, uma solicitação relacionada, o nome de um cookie, uma linha em um script ou arquivo html etc.).
  3. Detecção de problemas: Identifique os locais no navegador em que o problema pode ser detectado no Chrome e instrua o local para informar um problema, incluindo as informações relevantes da etapa (2).
  4. Salve e exiba os problemas. Armazene os problemas em um local adequado e disponibilize-os para o DevTools assim que ele for aberto.
  5. Como projetar o texto dos problemas. Crie um texto explicativo que ajude o desenvolvedor da Web a entender e, o mais importante, corrigir o problema

Etapa 1: definir histórias de usuários para problemas com a CSP

Antes de iniciarmos nosso trabalho de implementação, criamos um documento de design com histórias de usuários para entender melhor o que precisávamos fazer. Por exemplo, escrevemos a seguinte história de usuário:


Como desenvolvedor, que acabou de perceber que parte do meu site está bloqueada, eu quero:- - ...descobrir se a CSP é uma razão para iframes / imagens bloqueadas no meu site - ...saber qual diretiva da CSP causa o bloqueio de um determinado recurso - ...saber como alterar a CSP do meu site para permitir a exibição de recursos atualmente bloqueados / execução de js atualmente bloqueado.


Para explorar essa história de usuário, criamos alguns exemplos simples de páginas da Web que exibiam as violações da CSP nas quais estávamos interessados, e exploramos as páginas de exemplo para nos familiarizar com o processo. Veja algumas páginas da Web de exemplo (abra a demonstração com a guia Issues aberta):

Usando esse processo, aprendemos que o local da origem era a informação mais importante para depurar problemas de CSP. Também achamos útil encontrar rapidamente o iframe associado e a solicitação caso um recurso seja bloqueado. Além disso, um link direto para o elemento HTML no painel Elementos do DevTools também poderia ser útil.

Etapa 2: implementação de front-end

Transformamos esse insight no primeiro rascunho das informações que queríamos disponibilizar ao DevTools pelo protocolo do Chrome DevTools (CDP):

Confira abaixo o trecho de third_party/blink/public/devtools_protocol/browser_protocol.pdl (em inglês).

 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

A definição acima codifica uma estrutura de dados JSON. Ele é escrito em uma linguagem simples chamada PDL (linguagem de dados de protocolo). O PDL é usado para duas finalidades. Primeiro, usamos o PDL para gerar as definições do TypeScript que o front-end do DevTools depende. Por exemplo, a definição de PDL acima gera a seguinte interface do 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;
}

Em segundo lugar, e provavelmente mais importante, geramos uma biblioteca C++ a partir da definição que lida com a geração e o envio dessas estruturas de dados do back-end do Chromium em C++ para o front-end do DevTools. Com essa biblioteca, um objeto ContentSecurityPolicyIssueDetails pode ser criado usando este código C++:

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

Depois de decidir quais informações queríamos disponibilizar, precisávamos descobrir onde conseguir essas informações do Chromium.

Etapa 3: detecção de problemas

Para disponibilizar as informações para o Chrome DevTools Protocol (CDP) no formato descrito na última seção, precisávamos encontrar o local onde as informações estavam realmente disponíveis no back-end. Felizmente, o código da CSP já tinha um gargalo usado para o modo somente relatório, em que poderíamos usar: o ContentSecurityPolicy::ReportViolation informa problemas a um endpoint de relatório (opcional) que pode ser configurado no cabeçalho HTTP da CSP. A maioria das informações que queríamos relatar já estava disponível, portanto, nenhuma grande mudança no back-end foi necessária para que nossa instrumentação funcionasse.

Etapa 4: salvar e mostrar os problemas

Uma pequena complicação é o fato de que também queríamos relatar problemas que ocorreram antes da abertura do DevTools, semelhante à forma como as mensagens do console são tratadas. Isso significa que não informamos problemas imediatamente ao front-end, mas usamos um armazenamento preenchido com problemas, independentemente de o DevTools estar aberto ou não. Quando o DevTools for aberto (ou qualquer outro cliente CDP estiver anexado), todos os problemas registrados anteriormente poderão ser repetidos no armazenamento.

Isso concluiu o trabalho de back-end, e agora precisávamos nos concentrar em como revelar o problema no front-end.

Etapa 5: elaborar o texto dos problemas

Elaborar o texto dos problemas é um processo que envolve várias equipes além da nossa. Por exemplo, muitas vezes contamos com insights da equipe que implementa um recurso (neste caso, a equipe do CSP) e, claro, da equipe do DevRel, que projeta como os desenvolvedores da Web devem lidar com um determinado tipo de problema. O texto do problema geralmente passa por alguns ajustes até ser concluído.

Normalmente, a equipe do DevTools começa com um rascunho do que ela imagina:


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

Após a iteração, chegamos a:

ALT_TEXT_HERE

Como você pode ver, envolver a equipe de recursos e o DevRel torna a descrição muito mais clara e precisa.

Os problemas de CSP na sua página também podem ser encontrados na guia especificamente dedicada a violações da CSP.

Depuração de problemas de Tipos confiáveis

Trabalhar com a TT em grande escala pode ser um desafio sem as ferramentas para desenvolvedores certas.

Impressão em console aprimorada

Ao trabalhar com objetos confiáveis, queremos exibir pelo menos a mesma quantidade de informações que a contraparte não confiável. No momento, ao exibir um objeto confiável, nenhuma informação sobre o objeto unido é exibida.

Isso ocorre porque o valor exibido no console é extraído da chamada .valueOf() no objeto por padrão. No entanto, no caso do "Tipo confiável", o valor retornado não é muito útil. Em vez disso, queremos algo semelhante ao que você recebe ao chamar .toString(). Para isso, precisamos modificar o V8 e o Blink para introduzir um tratamento especial para objetos de tipo confiável.

Embora, por razões históricas, esse tratamento personalizado tenha sido feito no V8, essa abordagem tem desvantagens importantes. Há muitos objetos que exigem exibição personalizada, mas cujo tipo é o mesmo no nível do JS. Como o V8 é JS puro, ele não consegue distinguir conceitos que correspondem a uma API da web, como um tipo confiável. Por isso, o V8 precisa pedir ajuda ao incorporador (Blink) para distingui-los.

Portanto, mover essa parte do código para o Blink ou qualquer incorporador soa como uma escolha lógica. Além do problema exposto, há muitos outros benefícios:

  • Cada incorporado pode ter a própria geração de descrições
  • É muito mais fácil gerar a descrição pela API Blink
  • A API Blink tem acesso à definição original do objeto. Assim, se usarmos .toString() para gerar a descrição, não há risco de que .toString() possa ser redefinido.

Interrupção na violação (no modo somente relatório)

Atualmente, a única maneira de depurar violações de TT é definindo pontos de interrupção em exceções do JavaScript. Como as violações de TT aplicadas acionarão uma exceção, esse recurso pode ser útil. No entanto, em cenários reais, você precisa de um controle mais refinado sobre as violações de TT. Em particular, gostaríamos de quebrar apenas em violações de TT (não outras exceções), dividir também no modo somente relatório e distinguir entre os diferentes tipos de violações de TT.

O DevTools já oferece suporte a uma ampla variedade de pontos de interrupção, então a arquitetura é bastante extensível. A adição de um novo tipo de ponto de interrupção exige mudanças no back-end (Blink), no CDP e no front-end. É preciso introduzir um novo comando CDP, que será chamado de setBreakOnTTViolation. Esse comando será usado pelo front-end para informar ao back-end que tipo de violações de TT ele deve quebrar. O back-end, especificamente InspectorDOMDebuggerAgent, fornecerá uma "sondagem", onTTViolation(), que será chamada sempre que ocorrer uma violação de TT. Em seguida, InspectorDOMDebuggerAgent vai verificar se essa violação precisa acionar um ponto de interrupção. Nesse caso, ele vai enviar uma mensagem ao front-end para pausar a execução.

O que está sendo feito e o que vem a seguir?

Desde que os problemas descritos aqui foram introduzidos, a guia Problemas passou por algumas mudanças:

No futuro, planejamos usar a guia Issues para mostrar mais problemas, o que possibilitará descarregar o fluxo de mensagens de erro ilegíveis no console a longo prazo.

Fazer o download dos canais de visualização

Use o Chrome Canary, Dev ou Beta como seu navegador de desenvolvimento padrão. Esses canais de pré-lançamento oferecem acesso aos recursos mais recentes do DevTools, testam as APIs modernas de plataformas da Web e encontram problemas no site antes dos usuários.

Entrar em contato com a equipe do Chrome DevTools

Use as opções a seguir para discutir os novos recursos e mudanças na publicação ou qualquer outra coisa relacionada ao DevTools.

  • Envie uma sugestão ou feedback em crbug.com.
  • Informe um problema do DevTools em Mais opções   Mais   > Ajuda > Informar problemas no DevTools.
  • Envie um tweet em @ChromeDevTools.
  • Deixe comentários nos nossos vídeos do YouTube sobre a ferramenta DevTools ou nos vídeos do YouTube com dicas sobre o DevTools.