Segurança
Content-Security-Policy explicada: diretivas, unsafe-inline, nonces e uma implantação segura
A CSP é o cabeçalho de segurança mais poderoso e o mais fácil de errar. As diretivas que importam, como nonces e hashes substituem o 'unsafe-inline' e um plano de implantação que não quebra o seu site.
Nesta página
A Content-Security-Policy (CSP) é um cabeçalho de resposta que diz ao navegador de onde uma página pode carregar scripts, estilos, imagens, frames e outros recursos. O papel principal dela é conter danos: se um invasor conseguir injetar um <script> na sua página por meio de uma falha de cross-site scripting (XSS), uma boa política impede o navegador de executá-lo.
O porém é que "uma CSP" e "uma boa CSP" são coisas bem diferentes. Um cabeçalho cheio de curingas passa em um teste de presença e não protege quase nada. Este guia explica como distinguir uma da outra.
Como uma política é montada
Uma política é uma lista de diretivas separadas por ponto e vírgula. Cada diretiva indica um tipo de recurso e as fontes permitidas para ele: Content-Security-Policy: default-src 'self'; img-src 'self' https://images.example-cdn.com; object-src 'none'.
As fontes podem ser 'self' (a sua própria origem), origens específicas como https://js.stripe.com, esquemas como https: ou data:, palavras-chave como 'none', ou nonces e hashes, explicados mais abaixo. As palavras-chave vão entre aspas simples; as origens, não.
As diretivas mais importantes
default-src: o valor de reserva para a maioria dos tipos de recurso que você não lista separadamente.script-src: de onde os scripts podem vir. É a diretiva que de fato defende contra XSS, por isso merece o maior cuidado.style-src,img-src,font-src,connect-src(fetch, XHR, WebSockets),frame-srcemedia-src: os demais tipos de recurso.object-src 'none': bloqueia plugins como<object>e<embed>, um caminho antigo, mas ainda citado, para a execução de scripts.base-uri 'self'ou'none': impede que uma tag<base>injetada mude o destino das URLs relativas de scripts.frame-ancestors: quais sites podem incorporar a sua página em um frame — o substituto moderno do X-Frame-Options.form-action: para onde os formulários podem ser enviados.upgrade-insecure-requests: pede ao navegador que carregue por HTTPS os sub-recursos emhttp://.report-to/report-uri: para onde o navegador envia os relatórios de violação.
O que enfraquece uma política
'unsafe-inline'
Em script-src, 'unsafe-inline' libera todo bloco <script> inline e todo manipulador de evento inline, como onclick. É exatamente isso que um ataque de XSS injeta, de modo que uma política com 'unsafe-inline' para scripts mal chega a atrasá-lo. Os navegadores com suporte a nonces ou hashes ignoram 'unsafe-inline' quando há um deles presente, e é por isso que às vezes ele é mantido ao lado de um nonce, como reserva para navegadores muito antigos.
'unsafe-eval'
'unsafe-eval' libera eval(), new Function() e argumentos em formato de string para setTimeout. Ele transforma dados em código, o que é arriscado. Algumas bibliotecas e motores de template mais antigos precisam dele; os builds modernos, em geral, não.
Fontes amplas demais
*, https:, http: ou data: em script-src liberam scripts de praticamente qualquer lugar, de modo que um invasor pode hospedar o script dele em qualquer servidor HTTPS. As CDNs populares são um problema mais sutil: liberar o host inteiro de uma CDN pública permite a um invasor carregar qualquer biblioteca hospedada ali, inclusive versões antigas com formas conhecidas de burlar a política.
Report-Only sozinho
Content-Security-Policy-Report-Only reporta as violações, mas não bloqueia nada. É o jeito certo de começar e o lugar errado para parar.
Nonces e hashes: a saída para o 'unsafe-inline'
Um nonce é um valor aleatório que o seu servidor gera a cada resposta. Você o coloca no cabeçalho — script-src 'nonce-R4nd0mV4lue' — e em cada tag de script em que confia: <script nonce="R4nd0mV4lue">. O navegador executa apenas os scripts que trazem o nonce correspondente. Um script injetado não conhece o valor, por isso é bloqueado. O nonce precisa ser imprevisível e diferente em cada resposta; um nonce fixo não é melhor do que 'unsafe-inline'.
Um hash libera um script inline específico pelo hash SHA-256, SHA-384 ou SHA-512 do conteúdo dele: script-src 'sha256-…'. Os hashes servem bem a páginas estáticas, em que um nonce por resposta não é viável; qualquer alteração no script muda o hash.
Acrescentar 'strict-dynamic' permite que os scripts em que você confiou por nonce ou hash carreguem outros scripts e manda os navegadores compatíveis ignorarem as listas de hosts permitidos. Isso torna a chamada CSP estrita viável até em sites com gerenciadores de tags e widgets: script-src 'nonce-{random}' 'strict-dynamic'; object-src 'none'; base-uri 'none'.
Veja como a sua CSP é interpretada
O verificador de segurança gratuito do Rudra analisa a sua Content-Security-Policy diretiva por diretiva e sinaliza 'unsafe-inline', 'unsafe-eval', fontes de script amplas demais e a ausência de object-src ou base-uri.
Um plano de implantação que não quebra o seu site
- Faça o inventário do que a página carrega. Abra as ferramentas do desenvolvedor nas suas páginas principais — página inicial, login, checkout, páginas com conteúdo incorporado — e anote cada origem de script, estilo, fonte, frame e API.
- Envie uma política em Report-Only. Comece com algo como
default-src 'self'; script-src 'self' 'nonce-{random}'; object-src 'none'; base-uri 'self'emContent-Security-Policy-Report-Only. As violações aparecem no console do navegador e no seu endpoint de relatórios, se você configurar um. - Corrija ou libere cada violação. Mova os scripts inline para arquivos ou dê um nonce a eles, troque os manipuladores
onclickinline poraddEventListenere adicione as origens de terceiros específicas que você realmente usa. - Aplique a política. Quando os relatórios ficarem silenciosos nas suas principais jornadas, troque o nome do cabeçalho para
Content-Security-Policy. Você pode continuar enviando em paralelo uma política Report-Only mais rígida para testar o próximo passo. - Mantenha a política em dia. Todo novo widget, ferramenta de analytics ou provedor de pagamento exige uma mudança na política. Verifique de novo depois de adicionar um.
Defina o cabeçalho no servidor, na CDN ou no framework, e não em uma tag <meta>, se puder evitar: uma política em meta tag não pode usar frame-ancestors, relatórios nem o modo Report-Only.
O que o Rudra verifica na sua política
O verificador de segurança de site gratuito lê a CSP que a sua página realmente envia e mostra as diretivas interpretadas como evidência. Ele sinaliza a ausência de política, uma política enviada apenas como Report-Only, uma política que não restringe scripts de forma alguma (sem script-src nem default-src), 'unsafe-inline' na política de scripts (não sinalizado quando há um nonce ou um hash), 'unsafe-eval', fontes de script amplas demais, como * ou https: (a menos que você use 'strict-dynamic' com um nonce ou um hash), e — como notas informativas — a falta de object-src 'none', a falta de base-uri e os estilos inline. Ele reconhece frame-ancestors como proteção contra clickjacking quando o valor não é * nem um esquema solto.
Um verificador consegue dizer o que a sua política permite. Não consegue dizer se as suas páginas continuam funcionando com ela — só testar as suas jornadas reais em modo report-only responde a isso.
Perguntas frequentes
Uma Content-Security-Policy impede XSS?
Ela não corrige a falha de origem, mas uma política estrita impede a execução da maioria dos scripts injetados, o que limita o estrago. Você continua precisando escapar a saída e validar a entrada.
'unsafe-inline' em style-src é um problema?
É bem menos grave do que em script-src. Estilos injetados ainda podem ser explorados em alguns ataques, então removê-lo é um bom reforço de segurança, mas priorize primeiro a política de scripts.
Posso usar o mesmo nonce em todas as páginas?
Não. Um nonce precisa ser aleatório e único em cada resposta. Um nonce fixo ou reutilizado pode ser copiado por um invasor e não oferece proteção nenhuma.
Por quanto tempo devo usar o Report-Only antes de aplicar a política?
Tempo suficiente para cobrir os seus padrões reais de tráfego — muitas vezes de uma a duas semanas —, incluindo login, checkout e todas as páginas com conteúdo incorporado de terceiros. Aplique a política quando os relatórios só mostrarem ruído, como extensões de navegador.