EXEMPLOS DE CSP E IMPLANTAÇÃO

Exemplos de Content-Security-Policy: como montar uma CSP rígida sem quebrar o site

O cabeçalho Content-Security-Policy diz ao navegador quais scripts podem rodar nas suas páginas. Veja três políticas de exemplo que funcionam, o que torna uma política rígida (nonces, hashes e strict-dynamic), as diretivas que o default-src não cobre, os relatórios de violação e uma implantação por etapas que começa no modo Report-Only.

Ilustração conceitual de uma portaria de cerâmica marfim com uma grade de vidro: esferas de vidro com um selo menta passam e brilham, enquanto uma esfera cinza sem selo fica do lado de fora, ao lado de um carimbo de cobre.
Ilustração conceitual

A Content-Security-Policy (CSP) é um cabeçalho de resposta que diz ao navegador quais scripts, estilos, imagens, frames e conexões uma página pode usar. Se alguém conseguir enfiar um script no seu HTML, uma boa política impede que o navegador o execute. Políticas copiadas de fóruns costumam dar errado de dois jeitos: quebram o analytics, as fontes web e o widget de pagamento, ou vão sendo recheadas de 'unsafe-inline' e curingas até liberar quase tudo.

Este guia traz exemplos que funcionam para três tipos de site, explica o que torna uma política rígida (nonces, hashes e 'strict-dynamic'), passa pelas diretivas que o default-src não alcança, mostra como coletar relatórios de violação e descreve uma implantação que começa no modo Report-Only, para nada quebrar sem aviso. Todos os exemplos usam example.com; troque os valores de exemplo pelos seus.

Escopo deste guia

O verificador gratuito lê os cabeçalhos de uma única resposta pública: a página inicial da origem digitada, após até cinco redirecionamentos. Lê só o cabeçalho CSP aplicado, não uma política Report-Only, não avalia se a política serve para o seu site e não é um pentest.

1. O que uma Content-Security-Policy faz e o que ela não faz

Uma política é uma lista de diretivas separadas por ponto e vírgula. Cada diretiva indica um tipo de recurso e as origens permitidas para ele: script-src para JavaScript, style-src para CSS, img-src, font-src, connect-src para conexões fetch, XHR e WebSocket, e frame-src para os frames que a página incorpora. O default-src funciona como reserva para essas diretivas de busca quando falta uma específica (W3C CSP Level 3).

Três diretivas importantes não herdam nada do default-src: frame-ancestors (quais sites podem exibir sua página em um frame), base-uri (o que um elemento <base> pode definir) e form-action (para onde os formulários podem enviar dados). Por isso, uma política só com default-src 'self' ainda deixa qualquer site colocar sua página em um iframe e deixa uma tag <base> injetada desviar as URLs relativas.

Os valores de origem são de três tipos:

  • Palavras-chave entre aspas simples: 'self' (a própria origem da página), 'none' (nada), 'unsafe-inline', 'unsafe-eval' e 'strict-dynamic'.
  • Hosts e esquemas: https://cdn.example.com, https://*.example.com, https: ou data:.
  • Nonces e hashes: 'nonce-…' e 'sha256-…', que liberam elementos script ou style específicos em vez de hosts inteiros.

Envie a política como cabeçalho HTTP. Uma tag <meta http-equiv="Content-Security-Policy"> funciona para a maioria das diretivas, mas a especificação exclui ali frame-ancestors, report-uri e sandbox, e uma política Report-Only não pode ser entregue por meta de jeito nenhum (guia de CSP do MDN). Tenha expectativas realistas: a CSP limita o que um código injetado consegue fazer. Ela não substitui o escape da saída nem a validação da entrada, que são o que evita a injeção.

2. Três exemplos de Content-Security-Policy para começar

Exemplo A: um site sem scripts de terceiros. Uma política de lista de permissões funciona quando todo script e toda folha de estilo é um arquivo da sua própria origem:

Content-Security-Policy: default-src 'self'; img-src 'self' data:; object-src 'none'; base-uri 'self'; form-action 'self'; frame-ancestors 'self'; upgrade-insecure-requests

Ela bloqueia qualquer <script> inline, qualquer atributo style e tudo o que vem de outros hosts, então combina com sites feitos à mão ou gerados de forma estática, sem conteúdo incorporado.

Exemplo B: um site renderizado no servidor, com nonce. É a política rígida que o web.dev recomenda para páginas que o servidor gera a cada requisição, com proteção contra frames:

Content-Security-Policy: script-src 'nonce-R4nd0mBase64Value' 'strict-dynamic'; object-src 'none'; base-uri 'none'; frame-ancestors 'self'

O nonce muda a cada resposta (seção 3). Repare no que fica de fora: sem default-src, estilos, imagens, fontes e conexões ficam sem restrição. É uma escolha consciente: a política se concentra na execução de scripts, onde o cross-site scripting causa mais estrago, e evita listas de hosts que quebram toda vez que um fornecedor muda de domínio.

Exemplo C: páginas estáticas ou em cache, com hashes. Quando todo mundo recebe o mesmo HTML, libere os scripts inline pelo hash:

Content-Security-Policy: script-src 'sha256-BASE64_HASH_OF_INLINE_LOADER' 'strict-dynamic'; object-src 'none'; base-uri 'none'; frame-ancestors 'self'
Tipo de políticaCombina comTrabalho contínuoCuidado com
A: lista de hostsSites estáticos sem código de terceirosAtualizar a lista a cada novo fornecedorQualquer script de um host permitido pode ser carregado, inclusive versões antigas de bibliotecas em uma CDN compartilhada
B: nonce + strict-dynamicPáginas que sua aplicação gera a cada requisiçãoColocar o nonce em cada tag script dos templatesCaches de página que entregam o mesmo nonce para todos os visitantes
C: hash + strict-dynamicHTML estático, pré-renderizado ou em cache na bordaRecalcular os hashes sempre que o código inline mudarMinificadores e templates que mexem nos espaços e, portanto, no hash

Seja qual for a escolha, acrescente as diretivas de relatório da seção 6 e envie a política primeiro como Content-Security-Policy-Report-Only.

3. Nonces e hashes no lugar de 'unsafe-inline'

'unsafe-inline' libera qualquer script inline, inclusive o que um atacante injetar, e com isso some quase tudo o que a CSP oferece contra cross-site scripting. Nonces e hashes liberam só o código inline que você realmente quis publicar. Os navegadores ignoram 'unsafe-inline' em uma diretiva que também tem nonce ou hash (MDN), e é por isso que ele pode ficar em uma política rígida como reserva para navegadores muito antigos.

Nonces

Um nonce é um valor aleatório que o servidor gera a cada resposta, coloca no cabeçalho e copia no atributo nonce de cada script que pretende executar. Ele precisa ser imprevisível e novo a cada resposta; o web.dev recomenda pelo menos 128 bits de um gerador criptograficamente seguro. Em uma aplicação Node.js:

import crypto from 'node:crypto';

app.use((req, res, next) => {
  const nonce = crypto.randomBytes(16).toString('base64');
  res.locals.nonce = nonce;
  res.setHeader('Content-Security-Policy',
    `script-src 'nonce-${nonce}' 'strict-dynamic'; object-src 'none'; base-uri 'none'; frame-ancestors 'self'`);
  next();
});

O template então escreve <script nonce="…" src="/app.js"></script> com o mesmo valor. Coloque o nonce onde seus templates geram as suas próprias tags script, nunca reescrevendo todos os <script> do HTML pronto: uma troca geral também aprovaria um script injetado. Duas armadilhas são comuns: uma CDN ou um cache de página que guarda o HTML repete o mesmo nonce para todos os visitantes, e um valor gravado em um arquivo estático na hora do build não é um nonce.

Hashes

Um hash libera exatamente um script inline: o resumo SHA-256, SHA-384 ou SHA-512, codificado em base64, do texto entre as tags script. Cada caractere conta, inclusive espaços e quebras de linha, então uma mudança no minificador ou no template gera um hash novo. Por isso os hashes combinam melhor com páginas estáticas e em cache, cujo conteúdo é igual para todos. O Chrome mostra no console o hash que esperava quando bloqueia um script inline; você também pode calcular por conta própria:

printf '%s' 'console.log("hello")' | openssl dgst -sha256 -binary | openssl base64

O que nenhum dos dois libera

Manipuladores de evento inline, como onclick="…", e URLs javascript: são bloqueados por uma política baseada em nonce ou hash. Leve esse código para arquivos de script e associe com addEventListener. 'unsafe-hashes' pode liberar um manipulador específico pelo hash, mas use só como remendo temporário. Código que executa strings como código, como eval(), new Function() ou setTimeout com uma string, precisa de 'unsafe-eval'; substitua quando der e use o 'wasm-unsafe-eval', mais restrito, se só o WebAssembly precisar ser compilado (MDN script-src).

4. strict-dynamic: deixar os scripts confiáveis carregarem o que precisam

Quase todo site roda scripts que carregam outros scripts: um gerenciador de tags adiciona o analytics, um banner de consentimento adiciona os fornecedores dele, um widget de chat puxa o próprio pacote. Listar todos os hosts que eles podem usar é frágil. O 'strict-dynamic' segue outro caminho: um script liberado por nonce ou hash pode adicionar outros scripts, e eles também passam a ser confiáveis.

Nos navegadores que dão suporte, o 'strict-dynamic' também faz o navegador ignorar listas de hosts, 'self' e 'unsafe-inline' no script-src (MDN). Isso tem três consequências:

  • Cada tag <script> do seu HTML precisa do nonce ou de um hash correspondente, inclusive scripts da sua própria origem, porque o 'self' deixa de liberá-los.
  • Scripts que um script confiável cria com document.createElement('script') são permitidos. Os que são escritos na página com document.write (scripts inseridos pelo parser) não são, então trechos de incorporação antigos que dependem disso param de funcionar.
  • A confiança é repassada. Tudo o que o seu gerenciador de tags carregar vai rodar, ou seja, quem pode publicar nele pode, na prática, executar código no seu site. Trate esse acesso como acesso de deploy.

Para navegadores antigos, acrescente valores de reserva que os navegadores novos ignoram:

script-src 'nonce-R4nd0mBase64Value' 'strict-dynamic' https: 'unsafe-inline'; object-src 'none'; base-uri 'none'

Um navegador com suporte a CSP Level 3 aplica só o nonce e o 'strict-dynamic'. Um que entende nonces, mas não 'strict-dynamic', usa o nonce e https:, e um muito antigo cai em 'unsafe-inline' https:. O Google documenta um snippet do Tag Manager compatível com nonce, que repassa o nonce aos scripts que adiciona, e avisa que as variáveis de JavaScript personalizado do Tag Manager continuam exigindo 'unsafe-eval' (guia de CSP do Tag Manager).

5. frame-ancestors, object-src 'none' e base-uri: diretivas para escrever de forma explícita

  • frame-ancestors define quais sites podem incorporar sua página em um frame; é a defesa contra clickjacking. Use 'none' se ninguém precisa colocar a página em frame, 'self' se só as suas próprias páginas fazem isso, ou liste origens exatas como https://app.example.com. Ela só funciona no cabeçalho HTTP. O X-Frame-Options (DENY ou SAMEORIGIN) continua como reserva para navegadores antigos; faça os dois permitirem o mesmo enquadramento (MDN frame-ancestors).
  • object-src 'none' bloqueia <object> e <embed>. Os plugins sumiram dos navegadores modernos, mas esses elementos ainda podem carregar conteúdo, e uma política rígida sem default-src os deixa livres se você não disser nada.
  • base-uri 'none', ou 'self' se suas páginas usam um elemento <base>, impede que um <base href> injetado aponte todas as URLs relativas de scripts para outro host.
  • form-action 'self' limita para onde os formulários podem enviar dados. Inclua a origem do seu provedor de pagamento ou de login se algum formulário envia para lá, e teste esses fluxos depois da mudança.
  • upgrade-insecure-requests faz o navegador buscar por HTTPS os sub-recursos http://. Ajuda com conteúdo misto, mas não substitui o HSTS.

Escreva essas diretivas de forma explícita em todas as políticas, inclusive nos exemplos B e C. Elas quase não exigem manutenção e fecham brechas que as regras de scripts deixam abertas.

6. Relatórios de violação com report-to e report-uri

Os relatórios de violação contam o que uma política bloqueou, ou bloquearia no modo Report-Only, nos navegadores de visitantes reais. A CSP Level 3 define o report-to, que aponta para um endpoint declarado no cabeçalho Reporting-Endpoints, e torna obsoleto o antigo report-uri, que recebe uma URL diretamente. Segundo o MDN, o report-to está disponível nos navegadores atuais desde 2026, enquanto versões mais antigas só entendem o report-uri. Navegadores que suportam report-to ignoram o report-uri, então por enquanto envie os dois (MDN report-to):

Reporting-Endpoints: csp="https://example.com/csp-reports"
Content-Security-Policy-Report-Only: script-src 'nonce-R4nd0mBase64Value' 'strict-dynamic' 'report-sample'; object-src 'none'; base-uri 'none'; report-uri https://example.com/csp-reports; report-to csp

Os dois mecanismos enviam formatos diferentes. O report-uri manda um objeto JSON com a chave csp-report e o tipo de conteúdo application/csp-report. O report-to manda application/reports+json: uma lista de relatórios com type igual a csp-violation e um corpo com campos como documentURL, blockedURL, effectiveDirective e disposition (enforce ou report). Com 'report-sample', o relatório de um código inline bloqueado traz os primeiros 40 caracteres dele. Aceite os dois formatos no mesmo endpoint.

  • Conte com ruído. Extensões do navegador injetam scripts e geram violações com arquivos de origem como chrome-extension:// ou moz-extension://. Agrupe os relatórios por diretiva e host bloqueado e procure padrões, não casos isolados.
  • Trate os relatórios como dados pessoais. URLs de documento e referrers podem trazer parâmetros com e-mails ou tokens. Remova ou encurte esses trechos, guarde os relatórios por pouco tempo e não repasse sem motivo.
  • Proteja o endpoint. Qualquer pessoa pode enviar relatórios falsos: aplique limites de tamanho e de frequência, e nunca mostre o conteúdo sem escape em um painel administrativo.

7. O que costuma quebrar: analytics, fontes, scripts inline e conteúdo incorporado

A maioria das violações dos primeiros dias vem de poucas fontes. O campo effectiveDirective do relatório mostra qual regra olhar:

SintomaDiretiva no relatórioCorreção comum
O analytics para de registrar visualizaçõesscript-src-elem, connect-src, img-srcCarregar a tag com nonce, ou liberar o host do script, e liberar os hosts de coleta documentados pelo fornecedor (o Google publica a lista por produto)
As fontes web viram fontes do sistemafont-src, style-src-elemLiberar o host da folha de estilo em style-src e o dos arquivos de fonte em font-src (no Google Fonts, fonts.googleapis.com e fonts.gstatic.com), ou hospedar os arquivos você mesmo
Um trecho do tema ou do CMS para de funcionarscript-src-elem com blockedURL “inline”Colocar o nonce no template, mover o código para um arquivo ou liberar o hash dele
Botões com onclick não fazem nadascript-src-attrReescrever o manipulador com addEventListener em um arquivo de script
Variáveis personalizadas do gerenciador de tags não retornam nadascript-src (eval)Trocar por variáveis nativas ou aceitar 'unsafe-eval' como exceção registrada
Um vídeo, mapa ou frame de pagamento incorporado fica em brancoframe-srcIncluir a origem exata do fornecedor
Estilos inline são ignorados e o layout se desmontastyle-src-elem, style-src-attrMover os estilos para folhas de estilo, colocar o nonce nos elementos style ou manter 'unsafe-inline' só em style-src, como exceção registrada
Chat ou atualizações ao vivo não conectamconnect-srcIncluir os hosts https:// e wss:// do fornecedor
Seu app ou um parceiro não consegue mais incorporar a páginaframe-ancestorsListar as origens exatas que a incorporam

Estilos inline são um risco menor que scripts inline, mas não são inofensivos: CSS injetado pode mudar o que o visitante vê e, com seletores de atributo, vazar alguns dados da página. Muitos frameworks e bibliotecas de CSS-in-JS inserem elementos style em tempo de execução, então verifique se o seu aceita nonce antes de tirar 'unsafe-inline' do style-src. Hospedar fontes e bibliotecas no seu próprio servidor elimina várias dessas linhas de uma vez e deixa a política curta.

8. Como implantar uma CSP por etapas

  1. Inventário. Liste os scripts, estilos, fontes, frames e destinos de conexão das páginas principais: início, login, conta, checkout e páginas com widgets incorporados. O painel Network do DevTools e a configuração do seu gerenciador de tags são as fontes mais rápidas.
  2. Report-Only. Envie o rascunho como Content-Security-Policy-Report-Only com os relatórios ativados. Nada é bloqueado; as violações aparecem no console e no seu endpoint. Deixe rodando tempo suficiente para aparecerem as páginas menos visitadas e as campanhas, em geral uma semana ou mais.
  3. Corrija a origem, não a política. Inclua nonces, leve os manipuladores inline para arquivos, hospede as fontes no seu servidor. Acrescente um host ou 'unsafe-eval' só como exceção registrada, com responsável e motivo.
  4. Aplique. Troque o nome do cabeçalho para Content-Security-Policy. Mantenha ao lado uma política Report-Only mais rígida para testar o próximo aperto: quando os dois cabeçalhos chegam, o navegador aplica um e só relata o outro.
  5. Continue de olho. Uma nova tag de marketing, a atualização de um plugin ou de um framework pode trazer código inline novo. Mantenha o endpoint de relatórios ativo e confira o cabeçalho de novo depois de cada mudança no servidor, na CDN ou no framework.

Dois detalhes de infraestrutura causam muitas surpresas. Se uma CDN ou um framework acrescenta o próprio cabeçalho CSP, o navegador aplica todas as políticas recebidas e um recurso precisa passar em todas, então um cabeçalho extra só consegue endurecer, nunca afrouxar (MDN). E se o HTML fica em cache na borda, uma política com nonce exige gerar o nonce onde a página é montada, ou a troca por hashes. Para ver o que uma página realmente envia:

curl -sS -D - -o /dev/null https://example.com/ | grep -i content-security-policy

O guia para verificar os cabeçalhos de segurança trata dos cabeçalhos que acompanham a CSP, e o checklist de segurança para o lançamento do site os coloca em uma revisão completa antes de publicar.

9. Confira o cabeçalho publicado com o verificador gratuito

Com a política aplicada, o verificador de cabeçalhos de segurança gratuito mostra o que um navegador recebe da sua página inicial. Ele roda o snapshot gratuito de prontidão para lançamento do Sitelemetry, oito verificações passivas em um domínio público sem cadastro, e abre a verificação de cabeçalhos acima das outras sete.

  • Ele solicita a página inicial da origem que você digitar, segue até cinco redirecionamentos e lê a resposta final. Outras páginas, como login ou checkout, muitas vezes têm políticas diferentes; confira essas com o DevTools ou o curl.
  • Ele lê só o cabeçalho Content-Security-Policy aplicado. Na etapa Report-Only, ele continua apontando a CSP como ausente, com risco médio, porque uma política só de relatório não bloqueia nada. É o resultado esperado até você aplicar a política.
  • Uma diretiva frame-ancestors na política aplicada conta como proteção contra frames, assim como o X-Frame-Options.
  • Na nota de hardening, que é separada (de A a F), a CSP vale menos pontos quando o default-src ou uma diretiva script-src ou style-src (incluindo as variantes -elem e -attr) contém 'unsafe-inline', ou quando o default-src ou uma diretiva de script contém 'unsafe-eval'. A nota lê a política como ela está escrita, então um 'unsafe-inline' de reserva ao lado de um nonce também tira pontos, mesmo que os navegadores atuais o ignorem.
  • Ele não avalia nonces, hashes, 'strict-dynamic', object-src, base-uri nem os relatórios. Para isso, use o console do navegador e os seus relatórios de violação.

O resultado mostra uma pontuação e o resultado de cada uma das oito verificações, então o sinal da CSP aparece ao lado de HSTS, TLS e das demais verificações públicas.

Perguntas frequentes

Qual exemplo de Content-Security-Policy é bom para começar?

Para um site renderizado no servidor: script-src 'nonce-{aleatório}' 'strict-dynamic'; object-src 'none'; base-uri 'none'; frame-ancestors 'self', com um nonce novo a cada resposta. Para páginas estáticas ou em cache, use hashes dos seus scripts inline no lugar do nonce. Comece com qualquer uma delas como Content-Security-Policy-Report-Only.

Devo usar nonce ou hash na minha CSP?

Use nonce quando a sua aplicação gera cada página, porque ela consegue inserir um valor novo toda vez. Use hashes quando o HTML é estático ou fica em cache, porque um nonce em cache seria reaproveitado para todos os visitantes. Os dois funcionam junto com 'strict-dynamic'.

Ter 'unsafe-inline' no style-src é um problema?

É um risco menor do que 'unsafe-inline' no script-src, mas CSS injetado ainda pode mudar o que os visitantes veem e vazar alguns dados da página. Mantenha só como exceção registrada enquanto move os estilos para folhas de estilo ou coloca nonces nos elementos style.

Content-Security-Policy-Report-Only protege o meu site?

Não. Ele não bloqueia nada; só relata o que uma política aplicada bloquearia. Use para testar e depois mude para o cabeçalho Content-Security-Policy. Os dois podem ser enviados juntos, o que ajuda a testar a próxima versão, mais rígida.

Posso definir a CSP em uma tag meta?

Sim, para a maioria das diretivas. Mas frame-ancestors, report-uri e sandbox são ignorados em uma tag meta, e uma política Report-Only não pode ser entregue assim. Use o cabeçalho HTTP sempre que o seu servidor ou a sua hospedagem permitir.

Qual a diferença entre report-uri e report-to?

O report-uri recebe uma URL diretamente; a CSP Level 3 o declara obsoleto, mas navegadores antigos ainda dependem dele. O report-to aponta para um endpoint do cabeçalho Reporting-Endpoints e usa o formato da Reporting API. Navegadores que suportam report-to ignoram o report-uri, então enviar os dois não causa problema.

Fontes e leituras

  1. W3C: Content Security Policy Level 3www.w3.org
  2. MDN: guia de Content Security Policy (CSP)developer.mozilla.org
  3. MDN: cabeçalho Content-Security-Policydeveloper.mozilla.org
  4. MDN: diretiva CSP script-srcdeveloper.mozilla.org
  5. MDN: diretiva CSP frame-ancestorsdeveloper.mozilla.org
  6. MDN: diretiva CSP report-todeveloper.mozilla.org
  7. MDN: cabeçalho Reporting-Endpointsdeveloper.mozilla.org
  8. W3C: Reporting APIwww.w3.org
  9. web.dev: Mitigate cross-site scripting (XSS) with a strict Content Security Policy (CSP)web.dev
  10. Google Tag Platform: Use Tag Manager with a Content Security Policydevelopers.google.com
  11. OWASP Content Security Policy Cheat Sheetcheatsheetseries.owasp.org
Equipe Sitelemetry

Preparado pela equipe editorial do Sitelemetry. Consulte as fontes indicadas para aprofundar o assunto.

SITELEMETRY

Coloque o aprendizado em prática.

Explore a estrutura, as configurações e os sinais do seu site com o Sitelemetry.