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
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
| Diretiva | Governa | Recorre a default-src? |
|---|---|---|
script-src | elementos script, eval, handlers inline | sim |
style-src | elementos style, links de folha de estilo, @import, atributos style | sim |
connect-src | fetch, XMLHttpRequest, WebSocket, EventSource, sendBeacon | sim |
img-src / font-src / media-src / manifest-src | imagens e srcset; webfonts; áudio, vídeo, track; manifesto do app | sim |
object-src | object e embed — sempre 'none' | sim |
frame-src / child-src / worker-src | documentos aninhados; workers | sim, workers via child-src |
base-uri / form-action | valores que uma tag base pode definir; para onde formulários podem enviar | não |
frame-ancestors | quem pode incorporar esta página; substitui X-Frame-Options | não |
sandbox / require-trusted-types-for | flags de sandbox neste documento; sinks de DOM XSS | não |
report-uri / report-to | para onde os relatórios de violação são enviados por POST | nã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ão | Casa com | Cuidado |
|---|---|---|
'self' / 'none' | o esquema, host e porta exatos do documento / nada | não subdomínios; 'none' é nulo ao lado de qualquer outro valor |
https: | qualquer host naquele esquema | toda CDN da internet |
https://cdn.example.com | aquela origem, porta padrão implícita | prefixos de caminho não são impostos entre redirecionamentos |
*.example.com / * | qualquer subdomínio, em qualquer profundidade / qualquer host | não o example.com nu; * exclui data:, blob: |
'nonce-…' | elementos que carregam o atributo nonce correspondente | 128+ bits aleatórios, novos a cada resposta |
'sha256-…' | código inline cujos bytes exatos batem com este hash | um byte de espaço em branco muda o hash |
'strict-dynamic' | scripts criados por um script já confiável | origens de host e de esquema passam a ser ignoradas |
'unsafe-inline' | qualquer script ou estilo inline | ignorado 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.