Como Configurar Content Security Policy Estrita em SPAs
Aprenda a implementar uma Content Security Policy (CSP) estrita com nonces e strict-dynamic em aplicações SPA sem bloquear scripts de terceiros.
Como Configurar uma Content Security Policy Estrita em SPAs sem Quebrar Scripts de Terceiros
A segurança em aplicações de página única (Single Page Applications — SPAs) exige um equilíbrio delicado entre proteção contra vulnerabilidades e manutenção da funcionalidade. A Content Security Policy (CSP) é uma das camadas de defesa mais eficazes contra ataques de Cross-Site Scripting (XSS). No entanto, sua implementação tradicional costuma ser um desafio em ecossistemas modernos que dependem de bibliotecas dinâmicas e serviços de terceiros, como ferramentas de análise, gerenciadores de tags e gateways de pagamento.
Este artigo detalha como estruturar uma CSP estrita, baseada em nonces e na diretiva 'strict-dynamic', garantindo alta segurança sem interromper o funcionamento de scripts externos essenciais.
O Problema do Modelo Tradicional de Permissões (Allowlist)
Historicamente, as políticas de segurança de conteúdo eram configuradas liberando domínios específicos dos quais os scripts poderiam ser carregados:
http
Content-Security-Policy: script-src 'self' cdn.provedor.com scripts.analiticos.com;
Embora pareça uma abordagem lógica, esse modelo baseado em lista de permissões (allowlist) apresenta limitações graves em arquiteturas modernas:
- Manutenção complexa: Qualquer alteração no provedor de um script de terceiros ou inclusão de uma nova ferramenta exige atualização nas regras do servidor.
- Burlar a segurança (Bypass): Se um dos domínios autorizados hospedar um endpoint vulnerável (como uma API JSONP ou um serviço de redirecionamento aberto), um atacante pode injetar scripts maliciosos contornando a CSP totalmente.
- Incompatibilidade com SPAs: SPAs frequentemente injetam scripts de forma dinâmica em tempo de execução, o que dificulta o mapeamento prévio de todos os domínios de origem.
A Abordagem Estrita: Nonce e 'strict-dynamic'
A especificação CSP Level 3 introduziu um modelo de confiança baseado em criptografia e propagação dinâmica, eliminando a dependência de domínios específicos.
Os dois pilares dessa abordagem são:
- Nonce (Number used once): Um valor aleatório e imprevisível gerado pelo servidor a cada requisição HTTP. Apenas scripts contendo o atributo
nonceidêntico ao valor enviado no cabeçalho CSP serão executados. 'strict-dynamic': Uma diretiva que instrui o navegador a confiar em qualquer script carregado dinamicamente por um script que já foi autorizado (via nonce ou hash).
Essa combinação simplifica a integração com scripts de terceiros: você autoriza apenas o script inicial (por exemplo, o carregador do gerenciador de tags) usando o nonce, e o 'strict-dynamic' permite que ele carregue as dependências secundárias sem a necessidade de mapear cada domínio individualmente.
[IMAGEM] tipo: diagrama assunto: Fluxo de autorização de scripts em uma CSP estrita usando nonce e strict-dynamic motivo: Ilustra visualmente como o script inicial autenticado via nonce recebe permissão para carregar dependências secundárias de terceiros sem bloquear a aplicação. [/IMAGEM]
Passo a Passo: Configurando uma CSP Estrita em uma SPA
Passo 1: Geração de Nonce Único por Requisição
Para que o nonce seja seguro, ele deve ser gerado com um gerador de números pseudoaleatórios criptograficamente seguro (CSPRNG) a cada resposta do servidor. Nunca reutilize nonces entre requisições distintas.
Exemplo de lógica de geração no servidor (Node.js/Express):
```javascript const crypto = require('crypto');
app.use((req, res, next) => { res.locals.nonce = crypto.randomBytes(16).toString('base64'); next(); }); ```
Nota para SPAs Estáticas: Caso sua SPA seja servida estaticamente via CDN sem renderização no servidor (SSR), a abordagem baseada em nonce exige uma Edge Function ou Cloud Worker para injetar o nonce no HTML e no cabeçalho em tempo de borda. Caso contrário, a alternativa é utilizar hashes SHA-256 para os scripts inline estáticos.
Passo 2: Construção do Cabeçalho CSP
Com o nonce gerado, monte a diretiva script-src combinando a chave, o 'strict-dynamic' e fallbacks para navegadores antigos:
http
Content-Security-Policy:
script-src 'nonce-R4nd0mV4lu3' 'strict-dynamic' 'unsafe-inline' https:;
object-src 'none';
base-uri 'none';
Entendendo a lógica dos parâmetros:
- 'nonce-R4nd0mV4lu3': Autoriza o script inicial que possui este token exato.
- 'strict-dynamic': Confia nos scripts descendentes injetados pelo script inicial.
- 'unsafe-inline': Ignorado por navegadores modernos quando um nonce ou 'strict-dynamic' está presente, mas serve de fallback para navegadores antigos.
- https:: Serve como fallback de origem para navegadores que não suportam 'strict-dynamic' (CSP Level 1).
- object-src 'none': Desativa plugins antigos como Flash ou Java.
- base-uri 'none': Impede a manipulação da tag <base>, evitando redirecionamentos maliciosos de caminhos relativos.
Passo 3: Injeção do Nonce no HTML da SPA
No arquivo principal da sua SPA (index.html), insira o valor do nonce nas tags <script> que carregam a aplicação e as ferramentas de terceiros no servidor ou na Edge Function:
```html
```
Com essa estrutura, o script do gerenciador de tags executará com sucesso e, graças ao 'strict-dynamic', qualquer script adicional acionado por ele (ferramentas de análise, chat ao vivo ou pixels de conversão) funcionará sem ser bloqueado pela CSP.
Ajustando Outras Diretivas Essenciais
Enquanto a execução de scripts é resolvida pelo 'strict-dynamic', serviços de terceiros frequentemente fazem chamadas de rede (APIs) ou carregam recursos visuais. Essas ações são controladas por outras diretivas que ainda exigem especificação de origem ou padrões de permissão:
connect-src: Define para onde os scripts podem enviar dados (ex.:fetch,XMLHttpRequest,WebSocket). É necessário incluir os endpoints das APIs de terceiros utilizados por ferramentas analíticas ou de autenticação.img-src: Permite o carregamento de pixels de rastreamento e imagens remotas. O uso deimg-src 'self' data: https:costuma ser adequado para a maioria dos cenários.style-src: Para estilizações dinâmicas, pode-se usar nonces ou a opção'unsafe-inline'caso a biblioteca de terceiros injete estilos diretamente no DOM.
Exemplo de política completa:
http
Content-Security-Policy:
default-src 'self';
script-src 'nonce-R4nd0mV4lu3' 'strict-dynamic' 'unsafe-inline' https:;
style-src 'self' 'unsafe-inline';
img-src 'self' data: https:;
connect-src 'self' https://api.terceiro.com https://telemetria.analitica.com;
font-src 'self' data:;
object-src 'none';
base-uri 'none';
form-action 'self';
Estratégia de Implantação sem Quebrar a Aplicação
A aplicação de uma CSP em ambientes de produção exige validação prévia para evitar a interrupção de serviços.
1. Utilize o modo de relatório (Report-Only)
Antes de aplicar o bloqueio definitivo, altere o nome do cabeçalho para Content-Security-Policy-Report-Only. Nesse modo, o navegador não bloqueia recursos que violam a política, mas envia relatórios detalhados para o endpoint especificado.
http
Content-Security-Policy-Report-Only:
script-src 'nonce-R4nd0mV4lu3' 'strict-dynamic' 'unsafe-inline' https:;
report-uri /api/csp-violation-report;
2. Monitoramento e Ajustes
Analise os relatórios recebidos durante alguns dias de uso real. Fique atento a bloqueios em connect-src ou falhas na execução de scripts legados que não utilizam técnicas modernas de injeção dinâmica.
3. Aplicação Definitiva em Produção
Após confirmar que todas as rotas e integrações da SPA funcionam perfeitamente no modo de relatório, altere o cabeçalho de volta para Content-Security-Policy para ativar a proteção real.
Conclusão
A adoção de uma Content Security Policy estrita baseada em nonce e 'strict-dynamic' resolve a fricção histórica entre segurança robusta e a necessidade de integração com ecossistemas de terceiros. Em SPAs, essa abordagem elimina a necessidade de manter listas exaustivas de domínios, reduz drasticamente o vetor de ataques XSS e garante a execução fluida de scripts necessários para a operação do negócio.