csp

CSP ferramentas

Content-Security-Policy na prática: o que cada diretiva governa, como nonces, hashes e expressões de origem casam, e como implantar uma política com Report-Only.

1 ferramentas

§01 GUIA DO TEMA

O que uma política é de fato

Content-Security-Policy é um cabeçalho de resposta formado por diretivas, cada uma contendo uma lista de expressões de origem. Antes de o navegador buscar um sub-recurso, executar código inline, enviar um formulário ou deixar a página ser enquadrada, ele consulta a diretiva que governa aquilo; se nada casar, a requisição nunca acontece e uma violação é relatada. A aplicação acontece no navegador, e uma política só remove capacidades, nunca as concede.

O casamento funciona sobre origens — esquema, host, porta — e não sobre a URL que você tinha em mente: 'self' em https://app.example.com não cobre https://cdn.example.com nem seus subdomínios. Uma política também vale apenas tanto quanto a sua saída de emergência mais fraca: script-src 'self' 'unsafe-inline' entrega a quem consegue injetar marcação uma tag de script funcionando — exatamente o ataque que o CSP existe para impedir. Substitua 'unsafe-inline' por um nonce por resposta ou por um hash de conteúdo.

Políticas se compõem por interseção, nunca por união: quando uma resposta carrega dois cabeçalhos Content-Security-Policy — o seu mais um que uma CDN acrescentou — um recurso precisa satisfazer os dois, então um cabeçalho extra apenas aperta a página.

Referência de diretivas

DiretivaGovernaRecorre a default-src?
script-srcelementos script, eval, handlers inlinesim
style-srcelementos style, links de folha de estilo, @import, atributos stylesim
connect-srcfetch, XMLHttpRequest, WebSocket, EventSource, sendBeaconsim
img-src / font-src / media-src / manifest-srcimagens e srcset; webfonts; áudio, vídeo, track; manifesto do appsim
object-srcobject e embed — sempre 'none'sim
frame-src / child-src / worker-srcdocumentos aninhados; workerssim, workers via child-src
base-uri / form-actionvalores que uma tag base pode definir; para onde formulários podem enviarnão
frame-ancestorsquem pode incorporar esta página; substitui X-Frame-Optionsnão
sandbox / require-trusted-types-forflags de sandbox neste documento; sinks de DOM XSSnão
report-uri / report-topara onde os relatórios de violação são enviados por POSTnão

A coluna da direita morde: default-src 'self' ainda deixa abertos o sequestro da tag base, a exfiltração por formulário e o clickjacking. script-src-elem e script-src-attr separam elementos de handlers inline.

Expressões de origem

ExpressãoCasa comCuidado
'self' / 'none'o esquema, host e porta exatos do documento / nadanão subdomínios; 'none' é nulo ao lado de qualquer outro valor
https:qualquer host naquele esquematoda CDN da internet
https://cdn.example.comaquela origem, porta padrão implícitaprefixos de caminho não são impostos entre redirecionamentos
*.example.com / *qualquer subdomínio, em qualquer profundidade / qualquer hostnão o example.com nu; * exclui data:, blob:
'nonce-…'elementos que carregam o atributo nonce correspondente128+ bits aleatórios, novos a cada resposta
'sha256-…'código inline cujos bytes exatos batem com este hashum byte de espaço em branco muda o hash
'strict-dynamic'scripts criados por um script já confiávelorigens de host e de esquema passam a ser ignoradas
'unsafe-inline'qualquer script ou estilo inlineignorado quando há um nonce ou hash presente
'unsafe-eval' / 'unsafe-hashes'eval e new Function / hashes em onclick, style'wasm-unsafe-eval' é a versão restrita

Implantando com Report-Only

Publique a candidata como Content-Security-Policy-Report-Only: avaliada de forma idêntica, sem bloquear nada e servível ao lado de uma política em vigor, para que você possa apertar uma segunda enquanto a que está ativa protege os usuários. Colete violações com report-uri /csp-reports (obsoleto, mas universalmente suportado) ou com report-to, que exige um cabeçalho Reporting-Endpoints; a entrega de relatórios é isenta de connect-src. Bloqueios entre origens colapsam para uma origem em blocked-uri, nomeando o host recusado, não o arquivo; adicione 'report-sample' para obter um trecho de código e agrupe por effective-directive.

Qual ferramenta para qual trabalho

Quando um site está no ar, a pergunta é qual política é realmente entregue. Os Cabeçalhos HTTP buscam a URL a partir de api.sitekits.dev e devolvem status, contagem de redirecionamentos e todo cabeçalho de resposta, nunca o corpo — revelando um proxy que reescreveu a sua política. Endereços internos são recusados pela proteção contra SSRF.

Se em vez disso você está redigindo, o Gerador CSP oferece 14 campos de diretiva sobre uma base endurecida de default-src 'self'; frame-ancestors 'none'; base-uri 'self'; object-src 'none', mais uma caixa de seleção upgrade-insecure-requests que vem marcada por padrão — então um formulário intocado já emite aquela base com upgrade-insecure-requests anexado. A string é reconstruída conforme você digita, no navegador. Diretivas fora desses campos (report-to, sandbox, require-trusted-types-for) são acrescentadas à mão.

Montar a lista de permissões de um aplicativo existente é um problema de inventário: exporte um HAR do DevTools, leia-o no Visualizador HAR e depois passe as URLs surpreendentes pelo Parseador de URL para reduzir cada uma à forma esquema-host-porta que uma expressão de origem exige. Sanitize com o Sanitizador HAR antes de anexar a um ticket — HARs carregam cookies e cabeçalhos Authorization. O Diff de Texto mostra o que mudou entre as versões report-only e em vigor; o Formatador JSON torna um payload csp-report legível; e o Testador REST API chama o seu destino diretamente do navegador, sem servidor do sitekits no caminho — e sob a política da própria página dele, que relaxa o connect-src para 'self' https:, não sob a sua. Uma chamada que funciona lá mas falha dentro do seu aplicativo aponta para o seu próprio cabeçalho. Ela não vai nomear a causa: um bloqueio de connect-src e uma recusa de CORS aparecem ambos como um único TypeError do fetch, e a ferramenta imprime uma única mensagem que cobre os dois. Veja também: hub de HTTP, hub de segurança, kit do engenheiro de segurança.

Quebras comuns

Adicionar um nonce desativa silenciosamente o 'unsafe-inline'

Gerenciadores de tags e widgets de chat que injetam as próprias tags de script param de executar, já que nunca veem o seu valor por resposta; 'strict-dynamic' é a correção.

Hashes de CSP são base64 do digest, não hex

'sha256-…' quer base64 dos 32 bytes brutos do digest, então o hex vindo do Gerador de Hash passado pelo Base64 dá uma string errada. Copie o valor do erro no console do navegador.

Um nonce fixo no código é 'unsafe-inline' com passos extras

Nonces precisam ser regerados por resposta, o que os torna coisa de servidor; uma constante em um template — ou HTML em cache na borda enquanto o cabeçalho é regerado — é adivinhável. O Gerador UUID serve para testes manuais locais, nunca para um rollout.

Uma política em meta tag não consegue expressar metade do CSP

frame-ancestors, sandbox, report-uri e o modo Report-Only são ignorados em meta http-equiv, e a política cobre apenas a marcação que vem depois dela.

FAQ
Por que meu script inline continua bloqueado quando script-src tem 'unsafe-inline'?
Porque a mesma diretiva também contém um nonce ou um hash. Os navegadores ignoram deliberadamente o 'unsafe-inline' sempre que qualquer origem 'nonce-…' ou 'sha256-…' aparece naquela diretiva, então o fallback simplesmente deixa de valer. Ou você estampa o nonce atual no script inline, ou adiciona o hash dele, ou adiciona 'strict-dynamic' para que scripts carregados por um script já confiável herdem a confiança.
O default-src cobre todas as diretivas?
Não, e esse é o buraco mais comum em uma política que de resto é estrita. base-uri, form-action, frame-ancestors, sandbox, report-uri e report-to não recorrem ao default-src, então default-src 'self' sozinho ainda permite sequestro da tag base, envio de formulário para qualquer host e enquadramento por qualquer site. Essas precisam ser escritas explicitamente.
Como implanto CSP sem quebrar a produção?
Envie a candidata como Content-Security-Policy-Report-Only, que é avaliada exatamente como o cabeçalho real, não bloqueia nada e pode ser servida ao lado de uma política em vigor. Colete relatórios por uma ou duas semanas e depois troque o nome do cabeçalho. Espere uma fatia grande de ruído sem ação possível vindo de extensões de navegador, que injetam scripts inline nas suas páginas.
Devo usar um nonce ou um hash para scripts inline?
Use um nonce quando o HTML é gerado por resposta, já que o valor precisa ser imprevisível e novo a cada vez. Use hashes quando o HTML é estático ou fica em cache em uma CDN, porque um hash não exige aleatoriedade no servidor — mas ele cobre os bytes exatos do bloco inline, então um espaço alterado o invalida. Os dois combinam com 'strict-dynamic', que propaga confiança a partir do que a diretiva já permitia: script-src 'sha256-…' 'strict-dynamic' https: 'unsafe-inline' é a política estrita baseada em hash documentada para esse caso estático. A confiança alcança apenas os scripts que o bloco com hash cria em tempo de execução, não tags que já estão na marcação.
Por que meu relatório de violação nomeia apenas um domínio em blocked-uri em vez do arquivo que foi bloqueado?
Porque violações entre origens são deliberadamente reduzidas à origem — relatar a URL completa entregaria à página a informação que o CSP acabou de impedi-la de ler. Você descobre qual host foi recusado, não qual recurso, e depois de um redirecionamento você pode não receber nada além da origem inicial. Adicione 'report-sample' à diretiva para que os relatórios carreguem um trecho curto do código ofensor, e agrupe por effective-directive para ver qual regra está realmente disparando. Violações de mesma origem não são truncadas, então esses relatórios carregam o caminho completo.