Esta postagem do blog é sobre a implementação do suporte do DevTools para depurar problemas de política de segurança de conteúdo (CSP) com a ajuda da aba Issues, introduzida recentemente.
O trabalho de implementação foi feito no decorrer de dois estágios: 1. Na primeira, criamos o framework de relatórios geral e projetamos as mensagens de problema para três violações de CSP. 2. Na segunda, adicionamos problemas de tipo confiável e alguns recursos especializados do DevTools para depuração de 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 impedir scripts inline ou eval
, que reduzem a superfície de ataque para ataques de scripting em vários locais (XSS). Para uma introdução detalhada à CSP, acesse este link.
Uma CSP particularmente nova é a política Tipos confiáveis(TT), que permite uma análise dinâmica que pode impedir sistematicamente uma grande classe de ataques de injeção em sites. Para isso, o TT permite que um site controle o código JavaScript para permitir que apenas determinados tipos de coisas sejam atribuídos a sinks do 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 para 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 relatório: informa a mensagem de erro como um aviso, mas não causa falha na página da Web.
Implementar problemas da Política de Segurança de Conteúdo na guia Issues
O objetivo desse trabalho era melhorar a experiência de depuração para problemas de CSP. Ao considerar novos problemas, a equipe do DevTools segue este processo mais ou menos:
- Definir histórias de usuário. Identifique um conjunto de histórias de usuário no front-end do DevTools que aborda como um desenvolvedor da Web precisa investigar o problema.
- 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).
- Detecção de problemas. Identifique os locais no navegador em que o problema pode ser detectado no Chrome e prepare o local para informar um problema, incluindo as informações relevantes da etapa (2).
- Salvar e mostrar os problemas. Armazene os problemas em um lugar apropriado e disponibilize-os para o DevTools quando ele for aberto.
- Como criar o texto de problemas. Crie um texto explicativo que ajude o desenvolvedor da Web a entender e, mais importante, corrigir o problema.
Etapa 1: definir histórias de usuário para problemas da CSP
Antes de iniciarmos o trabalho de implementação, criamos um documento de design com histórias de usuário 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 é um motivo para iframes / imagens bloqueados 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 bloqueados no momento.
Para analisar esse caso de uso, criamos algumas páginas da Web de exemplo que mostravam as violações de CSP de que estávamos interessados e as analisamos para nos familiarizarmos com o processo. Veja algumas páginas da Web de exemplo. Abra a demonstração com a guia Problemas:
Usando esse processo, descobrimos que o local de origem era a informação mais importante para depurar problemas de CSP. Também achamos útil encontrar rapidamente o iframe e a solicitação associados caso um recurso tenha sido bloqueado. Além disso, um link direto para o elemento HTML no painel Elements do DevTools também pode ser útil.
Etapa 2: implementação do front-end
Transformamos esse insight no primeiro rascunho das informações que queríamos disponibilizar para o DevTools usando o Chrome DevTools Protocol (CDP):
Confira abaixo o trecho de 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
A definição acima codifica essencialmente uma estrutura de dados JSON. Ele é escrito em uma linguagem simples chamada PDL (Protocol Data Language). O PDL é usado para duas finalidades. Primeiro, usamos o PDL para gerar as definições do TypeScript em 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++ com base na definição que processa a geração e o envio dessas estruturas de dados do back-end do Chromium C++ para o front-end das Ferramentas do desenvolvedor. Usando essa biblioteca, um objeto ContentSecurityPolicyIssueDetails
pode ser criado com o seguinte 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, precisamos descobrir onde encontrar essas informações no Chromium.
Etapa 3: detecção de problemas
Para disponibilizar as informações para o protocolo do Chrome DevTools (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 do CSP já tinha um gargalo usado para o modo somente de relatório, em que podíamos conectar: ContentSecurityPolicy::ReportViolation
relata problemas a um endpoint de relatório (opcional) que pode ser configurado no cabeçalho HTTP do CSP. A maior parte das informações que queríamos informar já estava disponível, então não foram necessárias grandes mudanças no back-end para que a instrumentação funcionasse.
Etapa 4: salvar e mostrar os problemas
Uma pequena complicação é o fato de que também queríamos informar problemas que ocorreram antes de o DevTools ser aberto, semelhante à forma como as mensagens do console são tratadas. Isso significa que não informamos os problemas imediatamente para o front-end, mas usamos um armazenamento que é preenchido com problemas, independentemente de o DevTools estar aberto ou não. Quando o DevTools é aberto (ou qualquer outro cliente CDP é anexado), todos os problemas registrados anteriormente podem ser reproduzidos do armazenamento.
Isso concluiu o trabalho de back-end, e agora precisávamos nos concentrar em como mostrar o problema no front-end.
Etapa 5: projetar 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, seria a equipe de CSP) e, claro, da equipe de DevRel, que projeta como os desenvolvedores Web devem lidar com um determinado tipo de problema. O texto do problema geralmente passa por alguns refinamentos até ser concluído.
Normalmente, a equipe do DevTools começa com um rascunho do que 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:
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 descobertos na guia dedicada especificamente a violações de CSP.
Como depurar problemas de tipos confiáveis
Trabalhar com TT em grande escala pode ser desafiador sem as ferramentas de desenvolvedor certas.
Impressão do console aprimorada
Quando trabalhamos com objetos confiáveis, queremos mostrar pelo menos a mesma quantidade de informações que para o objeto não confiável. No momento, ao mostrar um objeto confiável, nenhuma informação sobre o objeto envolvido é mostrada.
O motivo é que o valor mostrado no console é retirado da chamada por padrão ao .valueOf()
no objeto. No entanto, no caso de Tipo confiável, o valor retornado não é muito útil. Em vez disso, gostaríamos de ter 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 motivos históricos, 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 pode distinguir conceitos que correspondem a uma API da Web, como um tipo confiável. Por isso, o V8 precisa pedir ajuda ao embedder (Blink) para diferenciá-los.
Portanto, mover essa parte do código para Blink ou qualquer embedder parece uma escolha lógica. Além do problema exposto, há muitos outros benefícios:
- Cada incorporador pode ter sua própria geração de descrição
- É muito mais fácil gerar a descrição usando a API Blink.
- O Blink tem acesso à definição original do objeto. Assim, se usarmos
.toString()
para gerar a descrição, não haverá risco de que.toString()
seja redefinido.
Break-on-violation (no modo somente relatório)
Atualmente, a única maneira de depurar violações de TT é definindo pontos de interrupção em exceções de JS. Como as violações de TT obrigatórias acionam uma exceção, esse recurso pode ser útil. No entanto, em cenários reais, você precisa de um controle mais preciso sobre as violações de TT. Em particular, queremos detalhar apenas as violações de TT (não outras exceções), fazer o mesmo no modo somente relatório e distinguir entre os diferentes tipos de violações de TT.
As DevTools já têm suporte para 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.
Vamos introduzir um novo comando de CDP, que vamos chamar de setBreakOnTTViolation
. Esse comando será usado pelo front-end para informar ao back-end sobre quais tipos de violações de TT ela deve violar. O back-end, especificamente InspectorDOMDebuggerAgent
, vai fornecer uma "sondagem", onTTViolation()
que será chamada sempre que ocorrer uma violação de TT. Em seguida, InspectorDOMDebuggerAgent
verifica se essa violação precisa acionar um ponto de interrupção. Se for o caso, ele vai enviar uma mensagem para o front-end para pausar a execução.
O que foi feito e o que vem a seguir?
Desde que os problemas descritos aqui foram introduzidos, a guia Problemas passou por algumas mudanças:
- A interconexão com outros painéis no DevTools foi aprimorada.
- A notificação de vários outros problemas foi movida para a guia Issues: low-contrast, trusted web-activity, quirks mode, API Attribution Reporting e problemas relacionados a CORS, entre outros.
- Foi introduzida uma oportunidade de ocultar problemas.
No futuro, planejamos usar a guia Problemas para mostrar mais problemas, o que vai permitir que o console seja descarregado do fluxo de mensagens de erro ilegíveis a longo prazo.
Fazer o download dos canais de visualização
Considere usar o Chrome Canary, Dev ou Beta como seu navegador de desenvolvimento padrão. Esses canais de visualização dão acesso aos recursos mais recentes do DevTools, permitem testar APIs de plataforma da Web de última geração e ajudam a encontrar problemas no seu site antes que os usuários.
Entre em contato com a equipe do Chrome DevTools
Use as opções a seguir para discutir os novos recursos, atualizações ou qualquer outra coisa relacionada ao DevTools.
- Envie feedback e solicitações de recursos em crbug.com.
- Informe um problema do DevTools usando a opção Mais opções > Ajuda > Informar um problema do DevTools no DevTools.
- Envie um tweet em @ChromeDevTools.
- Deixe comentários nos vídeos Novidades do DevTools no YouTube ou Dicas do DevTools no YouTube.