Helmet e CSP no Node.js ajudam a reduzir riscos comuns em aplicações web por meio de cabeçalhos HTTP de segurança. O Helmet configura vários headers úteis no Express, enquanto a Content Security Policy define quais origens podem fornecer scripts, estilos, imagens, fontes, frames e outros recursos. A combinação melhora a defesa contra XSS, clickjacking e carregamentos não autorizados, mas não substitui validação, escaping ou correção de vulnerabilidades no código.
Neste guia, você aprenderá a configurar Helmet e CSP de forma gradual, usar nonces em scripts legítimos, monitorar violações e evitar políticas tão permissivas que perdem o efeito. Para revisar a plataforma, consulte o que é Node.js e o tutorial de API com Node.js.
O que o Helmet faz?
Helmet é um middleware para aplicações compatíveis com Express. Ele adiciona ou ajusta headers como Content-Security-Policy, Cross-Origin-Opener-Policy, Referrer-Policy, Strict-Transport-Security e X-Content-Type-Options. Cada header protege uma fronteira diferente. Por isso, a configuração deve considerar se a aplicação serve HTML, funciona apenas como API, usa recursos de terceiros ou precisa ser incorporada em frames.
Não trate o pacote como uma caixa mágica. Um header pode bloquear funcionalidades legítimas, enquanto uma diretiva ampla pode permitir exatamente o conteúdo que deveria ser restringido. Leia a documentação e teste a resposta final depois de proxies, CDN e balanceadores.
Instalação e configuração inicial
npm init -y
npm install express helmet
npm install --save-dev supertestCrie uma configuração explícita, principalmente para CSP. Em desenvolvimento, ferramentas de hot reload podem exigir regras diferentes. Não leve permissões temporárias para produção.
import crypto from 'node:crypto';
import express from 'express';
import helmet from 'helmet';
const app = express();
app.use((req, res, next) => {
res.locals.cspNonce = crypto.randomBytes(16).toString('base64');
next();
});
app.use(helmet({
contentSecurityPolicy: {
directives: {
defaultSrc: ["'self'"],
scriptSrc: [
"'self'",
(req, res) => `'nonce-${res.locals.cspNonce}'`
],
styleSrc: ["'self'"],
imgSrc: ["'self'", 'data:', 'https:'],
fontSrc: ["'self'"],
connectSrc: ["'self'", 'https://api.example.com'],
objectSrc: ["'none'"],
baseUri: ["'self'"],
frameAncestors: ["'none'"],
formAction: ["'self'"]
}
},
referrerPolicy: { policy: 'strict-origin-when-cross-origin' }
}));O nonce muda a cada resposta e deve ser incluído apenas nos elementos de script autorizados. Ele não pode ser previsível nem reutilizado como valor fixo de configuração.
Como usar o nonce no HTML
Ao renderizar uma página no servidor, insira o nonce no atributo correspondente. Não monte o HTML com dados do usuário sem escaping.
app.get('/', (req, res) => {
const nonce = res.locals.cspNonce;
res.type('html').send(`
<!doctype html>
<html lang="pt-BR">
<body>
<h1>Painel</h1>
<script nonce="${nonce}" src="/app.js"></script>
</body>
</html>
`);
});Scripts sem o nonce correto serão bloqueados pela política. Evite adicionar 'unsafe-inline' apenas para resolver rapidamente um erro, pois isso reduz a proteção contra injeção de script.
Política para APIs
Uma API que nunca entrega HTML pode desativar a CSP do Helmet, já que navegadores aplicam a política a documentos. Ainda assim, outros headers continuam úteis. Uma configuração de API deve revisar CORS, HSTS, MIME sniffing e exposição de informações. Leia também CORS em APIs Node.js e HTTPS e TLS no Node.js.
Comece com Report-Only
Em uma aplicação existente, ativar uma política restritiva diretamente pode quebrar scripts, fontes e integrações. Use inicialmente Content-Security-Policy-Report-Only para observar o que seria bloqueado. Analise os relatórios, remova dependências desnecessárias e depois aplique a política real.
app.use(helmet.contentSecurityPolicy({
reportOnly: true,
directives: {
defaultSrc: ["'self'"],
scriptSrc: ["'self'"],
objectSrc: ["'none'"],
reportUri: ['/csp-report']
}
}));O endpoint de relatório deve limitar tamanho, validar JSON e aplicar rate limiting. Navegadores e extensões podem gerar ruído; não considere cada evento uma invasão confirmada.
Diretivas importantes
- default-src: fallback para tipos sem diretiva específica;
- script-src: controla JavaScript e deve ser o foco principal;
- style-src: controla CSS e estilos inline;
- connect-src: limita fetch, WebSocket e EventSource;
- img-src: controla imagens e esquemas permitidos;
- frame-ancestors: define quem pode incorporar a página;
- object-src: normalmente deve ser
'none'; - base-uri: impede alteração maliciosa da base de URLs;
- form-action: restringe destinos de formulários.
Evite listas enormes de domínios
Permitir muitos hosts aumenta a superfície de confiança. Um domínio de CDN comprometido ou um endpoint que aceite upload pode tornar a regra insuficiente. Prefira recursos locais, integridade de sub-recurso quando aplicável e nonces ou hashes para scripts estritamente controlados.
Validação e segurança complementar
CSP é uma camada adicional. Continue escapando HTML, usando templates seguros, validando URLs e evitando APIs perigosas como innerHTML com conteúdo não confiável. O artigo sobre proteção contra ataques web apresenta outras medidas importantes.
- não coloque segredos em HTML ou JavaScript;
- use cookies Secure, HttpOnly e SameSite;
- valide uploads e Content-Type;
- restrinja frames e formulários;
- mantenha dependências atualizadas;
- trate CSP como defesa em profundidade.
Testes automatizados
Teste os headers nas rotas que servem documentos e nas respostas de erro. Confirme que o nonce está presente na política e no HTML, muda entre requisições e não aparece em scripts não autorizados.
import test from 'node:test';
import assert from 'node:assert/strict';
import request from 'supertest';
import { app } from './app.js';
test('envia CSP restritiva', async () => {
const response = await request(app).get('/');
const csp = response.headers['content-security-policy'];
assert.match(csp, /default-src 'self'/);
assert.match(csp, /object-src 'none'/);
assert.match(csp, /nonce-/);
});
test('gera nonce diferente', async () => {
const first = await request(app).get('/');
const second = await request(app).get('/');
assert.notEqual(
first.headers['content-security-policy'],
second.headers['content-security-policy']
);
});Integre testes de navegador para confirmar que recursos permitidos carregam e recursos não autorizados são bloqueados. Testes unitários com Jest também podem validar funções de configuração.
Observabilidade
Registre relatórios CSP com requestId, diretiva violada, origem do documento e recurso bloqueado. Remova query strings e dados pessoais antes de armazenar. Métricas devem agregar por diretiva e ambiente, evitando transformar URLs completas em labels.
Erros comuns
- Adicionar unsafe-inline: resolve o sintoma e enfraquece a proteção;
- Usar nonce fixo: um valor previsível pode ser reutilizado por conteúdo injetado;
- Confiar apenas no Helmet: o pacote não corrige XSS no código;
- Ignorar proxies: a resposta final pode ter headers removidos ou duplicados;
- Ativar tudo sem testes: recursos legítimos podem parar de funcionar;
- Permitir hosts amplos: subdomínios comprometidos passam a ser confiáveis.
Checklist de produção
- CSP foi testada em modo Report-Only;
- script-src não usa unsafe-inline;
- nonce é criptográfico e muda por resposta;
- object-src está bloqueado;
- frame-ancestors e form-action estão definidos;
- origens externas foram justificadas;
- relatórios possuem limites e sanitização;
- headers foram verificados após CDN e proxy.
Referências oficiais
Conclusão
Helmet facilita a configuração de cabeçalhos de segurança, e a CSP cria uma lista executável das fontes autorizadas para o documento. A melhor estratégia é começar restritivo, introduzir permissões somente quando justificadas, usar nonces em scripts legítimos e observar violações antes do bloqueio definitivo.
Uma política sólida não substitui código seguro, mas limita o impacto de várias falhas. Com testes, relatórios, revisão de dependências e configuração coerente em todos os ambientes, Helmet e CSP tornam a aplicação Node.js mais resistente sem depender de regras genéricas e permissivas.




