Pular para o conteúdo
Development

O CSP que só quebrou em produção

Por Victor Da Luz
securitycloudflareastrodev-logsite

Adicionar headers de segurança devia ser a tarefa chata. Não existia nenhum header, então eu já tinha uma política sugerida elaborada num spike anterior, e a tarefa parecia ser só copiar e colar uma string de Content-Security-Policy num arquivo de config. Não ficou chata por muito tempo.

A primeira surpresa foi estrutural. Este site é Astro rodando em Cloudflare Workers, e o plano previa dois mecanismos: middleware para páginas renderizadas no servidor, um arquivo _headers para as estáticas. Fui escrever o middleware e primeiro conferi uma coisa: toda rota deste site é prerenderizada. Não a maioria, todas. Middleware no Astro só roda para páginas renderizadas sob demanda, e não existe nenhuma aqui. Escrevi mesmo assim, para quando uma rota dinâmica de verdade aparecer, mas rotulei com honestidade: hoje ele não faz nada. O arquivo _headers é a única coisa que toca uma requisição real.

A segunda surpresa veio de realmente ler a documentação da Cloudflare em vez de supor que eu já entendia a sintaxe do _headers. Eu queria uma política estrita em todo lugar, e uma mais permissiva só para a pasta que guarda um jogo Unity WebGL, já que WebAssembly precisa de permissões que uma página normal não precisa. Meu instinto foi adicionar uma segunda regra para aquela pasta com as permissões extras anexadas. A documentação da Cloudflare me impediu: regras que combinam com a mesma requisição não se sobrescrevem, elas se combinam, e headers duplicados são unidos com vírgulas. Um navegador lendo dois CSPs unidos por vírgula não escolhe um, ele aplica a interseção, a versão mais estrita de cada regra. Minha política “mais permissiva” para o jogo teria sido silenciosamente estrangulada de volta à estrita. Existe uma sintaxe real de remoção para isso, um ponto de exclamação antes do nome do header, que limpa o que veio antes para você poder escrever algo genuinamente diferente. Sem checar isso, eu teria lançado uma política que parecia permissiva no arquivo e se comportava exatamente tão restritiva quanto aquela que ela supostamente estava afrouxando.

Mesmo com isso corrigido, o header parecer correto num arquivo de texto não me dizia nada sobre se o jogo realmente rodaria. Então carreguei. Chrome headless, apontado direto para o documento HTML do próprio jogo, lendo o que o console realmente dizia. A primeira execução falhou na hora: o jogo embute seu próprio scriptzinho inline, e minha política não permitia scripts inline. Abri a regra de novo e permiti, só para aquele caminho. Segunda execução, mais tarde, falhou de novo, dessa vez mais fundo - a Unity sobe um web worker a partir de uma blob URL, e acontece que a permissão para criar o worker não é a mesma permissão que o worker precisa para carregar seu próprio código. Essa segunda fica sob script-src, não worker-src, e nada na primeira falha teria me indicado isso; só encontrei porque deixei a página rodar tempo suficiente para chegar naquele ponto.

A última falha foi a que nenhum teste local poderia ter pego. Tudo passou contra o runtime real da Cloudflare rodando na minha máquina. Fiz o merge, o deploy, e apontei um navegador headless para o site em produção de verdade para fechar o ciclo - e falhou em algo novo: a própria Cloudflare injeta um script de analytics em toda resposta real, um passo que só acontece no edge deles, nunca no preview local. Minha política não sabia que aquele script existia porque não havia como saber, a não ser batendo em produção.

Quatro falhas diferentes, quatro motivos diferentes pelos quais um header que “parecia correto” não era, e cada uma delas só apareceu porque insisti em observar o carregamento real da página em vez de confiar que o arquivo estava certo. A última correção saiu como um push rápido de acompanhamento depois que o site já estava no ar, liberou o script extra, e confirmou tudo limpo.

Leitura relacionada

Você também pode achar útil

Airalo

eSIM Airalo

eSIM de dados local para viagens - sem necessidade de trocar um SIM físico.

Este é meu link de indicação da Airalo. Você recebe um desconto no seu primeiro eSIM e eu ganho crédito da Airalo para o meu.

Saiba mais
Proton

Proton Pass

Gerenciador de senhas focado em privacidade, da equipe por trás do Proton Mail.

Como parceiro da Proton, ganho com compras qualificadas dos serviços de privacidade e segurança da Proton (Pass, Mail, VPN, Drive).

Saiba mais
AdGuard

AdGuard para iOS

Bloqueio de anúncios e rastreadores em todo o sistema no iOS, sem necessidade de um servidor DNS separado.

Como afiliado da AdGuard, ganho com compras qualificadas.

Saiba mais