22 de nov. de 2025·8 min de leitura

Cabeçalhos de segurança para aplicações web: CSP, HSTS, iframe e testes rápidos

Cabeçalhos de segurança para aplicações web explicados: o que CSP, HSTS e cabeçalhos de framing fazem, padrões seguros para começar e testes rápidos que não quebram scripts.

Cabeçalhos de segurança para aplicações web: CSP, HSTS, iframe e testes rápidos

Por que cabeçalhos seguros importam (e por que às vezes quebram apps)

“Endurecer o navegador” frequentemente se resume a alguns cabeçalhos de resposta que dizem ao navegador para recusar comportamentos arriscados. Sua app ainda pode ter bugs, mas o navegador recebe regras mais claras sobre o que pode carregar, onde pode ser incorporado e se pode voltar para HTTP sem criptografia.

É por isso que um app pode parecer ok em desenvolvimento e falhar numa revisão de segurança em produção. No dev você costuma estar no localhost, tem menos scripts de terceiros e padrões mais permissivos. Em produção você adiciona domínios reais, CDNs, analytics, widgets de pagamento e SSO. Esses extras são exatamente o que os cabeçalhos de segurança podem bloquear se você os configurar com agressividade demais.

Quando esses cabeçalhos são configurados corretamente, eles reduzem riscos comuns, como cross-site scripting (XSS) escalando para tomada de conta, clickjacking (sua app em um frame para enganar usuários), ataques de downgrade para HTTP e vazamento de dados para domínios de terceiros inesperados.

O problema: um único cabeçalho “errado” pode quebrar fluxos importantes. uma Content Security Policy (CSP) muito rígida pode bloquear o script que renderiza o botão de login, o iframe usado por um provedor de pagamento ou uma tag de analytics que só carrega após o consentimento. HSTS também pode surpreender equipes. Se você ativá-la antes do HTTPS estar totalmente correto, pode travar usuários em HTTPS e fazer o site parecer “indisponível” para eles.

Um exemplo realista: um protótipo gerado por IA funciona numa URL de preview, mas a produção adiciona um domínio customizado e autenticação real. Então a CSP bloqueia o script de callback de auth, ou as regras de framing impedem o checkout embutido. FixMyMess vê isso com frequência, por isso os cabeçalhos devem ser aplicados gradualmente, não simplesmente ativados e esquecidos.

Os três cabeçalhos que a maioria das pessoas quer dizer: CSP, HSTS e framing

A maioria das conversas sobre cabeçalhos de segurança se resume a três controles relacionados a três riscos:

  • Controlar o que o navegador pode executar (CSP)
  • Controlar como o navegador conecta (HSTS)
  • Controlar onde suas páginas podem aparecer (X-Frame-Options ou frame-ancestors do CSP)

1) CSP (Content-Security-Policy)

CSP diz ao navegador quais scripts, estilos, imagens e conexões são permitidos. Seu papel principal é limitar os danos de XSS bloqueando código inesperado de executar.

Também é o cabeçalho mais propenso a quebrar coisas no início, especialmente tags de analytics, scripts inline e widgets de terceiros.

2) HSTS (Strict-Transport-Security)

HSTS instrui o navegador a sempre usar HTTPS para o seu site por um período de tempo. Protege contra SSL stripping e acessos acidentais por HTTP, forçando conexões cifradas.

Ative-o apenas quando sua app estiver totalmente em HTTPS. Depois que um navegador armazenar HSTS para seu domínio, não há como desfazer rapidamente para aquele usuário.

3) Proteção de framing (X-Frame-Options e frame-ancestors)

Cabeçalhos de framing controlam se outros sites podem incorporar suas páginas em um iframe. Defendem contra clickjacking impedindo que uma página maliciosa coloque sua UI sob uma sobreposição enganosa.

Você verá duas opções:

  • X-Frame-Options: mais antigo e simples (DENY ou SAMEORIGIN)
  • frame-ancestors (dentro do CSP): mais novo, mais flexível, geralmente preferido quando você já usa CSP

Se sua app nunca deve ser incorporada, o padrão “sem framing” é geralmente o correto. Se você precisa incorporá-la intencionalmente (por exemplo, dentro de um portal de parceiro), trate isso como uma decisão de allowlist cuidadosa.

Noções básicas de CSP sem o jargão

CSP é o cabeçalho que diz ao navegador o que sua página pode carregar e executar. É também o que mais tende a causar quebra súbita, porque pode bloquear JavaScript que antes rodava livremente.

Pense na CSP como um segurança na entrada. Scripts, estilos, imagens, fontes e frames só entram se corresponderem às regras. Se sua app depende de um snippet inline, uma tag de analytics ou um widget de terceiro, a CSP vai pará-lo até você permitir explicitamente.

Duas diretivas aparecem constantemente:

  • default-src é a regra fallback. Se você a definir rígida, qualquer coisa não coberta por uma regra específica será bloqueada.
  • script-src controla de onde os scripts podem carregar e que tipos de execução são permitidos. É onde a quebra costuma aparecer primeiro.

A maioria das surpresas vem dos mesmos padrões:

  • Scripts inline (código dentro do HTML) são bloqueados a menos que você use um nonce/hash ou permita 'unsafe-inline' (não é um bom plano a longo prazo).
  • eval() e caminhos de código dinâmico são bloqueados a menos que você permita 'unsafe-eval' (comum em bibliotecas antigas e algumas ferramentas).
  • Tags de terceiros (chat, analytics, testes A/B) carregam de domínios que você não autorizou, ou injetam código inline.

Um passo inicial mais seguro é usar Content-Security-Policy-Report-Only. O navegador reporta violações mas não as bloqueia, assim você vê o que quebraria antes dos usuários serem afetados.

Um exemplo rápido: se um protótipo do Lovable ou Replit “funciona” porque depende de scripts inline, o modo Report-Only vai expor isso imediatamente. As equipes frequentemente trazem esses achados para a FixMyMess para que padrões arriscados sejam substituídos por scripts com nonce e o uso escondido de eval() seja removido sem mudar a experiência do app.

Configurações de CSP para começar (depois aperte)

CSP é um dos cabeçalhos mais úteis e uma das maneiras mais fáceis de quebrar uma UI funcionando se você for rígido demais no primeiro dia. Comece com uma política que bloqueie o pior, depois a aperte conforme aprender o que sua app realmente carrega.

Aqui está uma CSP “inicial” razoável que costuma funcionar para apps típicos (especialmente SPAs) enquanto adiciona proteção:

Content-Security-Policy: default-src 'self'; base-uri 'self'; object-src 'none'; frame-ancestors 'none'; img-src 'self' data: https:; font-src 'self' https: data:; script-src 'self' 'unsafe-inline' 'unsafe-eval' https:; style-src 'self' 'unsafe-inline' https:; connect-src 'self' https:; upgrade-insecure-requests

Isto ainda permite comportamentos arriscados ('unsafe-inline' e 'unsafe-eval') para reduzir a chance de quebrar a app. O objetivo é remover esses permissivos de forma segura, uma mudança por vez.

Aperte gradualmente

Faça uma edição, faça deploy e monitore os erros no console do navegador.

  • Tranque scripts primeiro: substitua 'unsafe-inline' por nonces e remova 'unsafe-eval' quando possível.
  • Reduza connect-src: mantenha apenas as APIs e endpoints de terceiros que você realmente usa.
  • Limite img-src e font-src: mantenha https: se você usa CDNs; remova data: se não precisar.
  • Se precisar incorporar algo (pagamentos, docs), defina regras de framing intencionalmente em vez de afrouxar tudo.

Nonces vs hashes (uma regra simples)

Use nonces quando seus templates HTML gerarem scripts/estilos inline em tempo de execução (comum em apps renderizados no servidor). Use hashes quando o snippet inline for estável e raramente muda.

Para analytics, fluxos de pagamento e widgets de suporte, espere adicionar domínios específicos a script-src e connect-src. Se um protótipo gerado por IA tem scripts inline confusos, pode ser necessário um rápido trabalho de limpeza (um trabalho comum da FixMyMess) antes de apertar a CSP sem surpresas.

HSTS: proteja o HTTPS sem se trancar fora

HSTS diz aos navegadores “use sempre HTTPS para este site”. Depois que um navegador vê o cabeçalho uma vez, ele reescreve visitas futuras para HTTPS mesmo que alguém digite http:// ou clique num link antigo. Isso é ótimo para impedir downgrade attacks, mas também pode travar usuários em HTTPS se sua configuração não for estável.

Depois que um navegador armazenar HSTS para seu domínio, não há como desfazer rapidamente para aquele usuário. Se o HTTPS quebrar amanhã (certificado expirado, redirect ruim, balanceador mal configurado), esses usuários podem ficar sem acesso até você consertar.

Adie HSTS se qualquer um destes for verdade:

  • Você tem conteúdo misto (imagens, scripts ou chamadas API em HTTP) que não foi corrigido.
  • Redirects são inconsistentes entre páginas.
  • Staging compartilha o mesmo domínio ou subdomínios que usuários podem visitar num navegador real.
  • Você ainda está testando certificados, configurações de CDN ou domínios customizados.

Um ponto de partida seguro é um max-age pequeno (1 hora a 1 dia). Após alguns deploys limpos, aumente para uma semana, depois um mês, depois 6–12 meses quando estiver confiante.

Tenha cuidado com duas flags:

  • includeSubDomains significa que todos os subdomínios devem suportar HTTPS (admin, API, landing pages antigas, subdomínios esquecidos).
  • preload é um compromisso ainda maior. Serve para domínios maduros com HTTPS permanente, não para protótipos.

Se você está herdando uma app construída por IA, verifique redirects e conteúdo misto primeiro. Equipes frequentemente pedem à FixMyMess para auditar isso porque um cabeçalho errado num protótipo instável pode transformar um bug pequeno num outage.

Proteção de framing: X-Frame-Options e frame-ancestors

Rebuild fast, then harden security
If the prototype is too brittle, we can rebuild cleanly while keeping your product behavior.

Clickjacking acontece quando sua app é carregada dentro de um frame oculto ou enganoso no site de outra pessoa. O usuário pensa que está clicando num botão inocente, mas na verdade está clicando no seu botão “Deletar conta” ou “Transferir” por baixo.

Para evitar isso, use X-Frame-Options e/ou CSP frame-ancestors. X-Frame-Options é antigo e limitado. Pode dizer “deny” (nunca me enquadre) ou “sameorigin” (só posso ser enquadrado pelo mesmo site). frame-ancestors é a escolha moderna porque permite allowlists precisas.

Um padrão seguro para a maioria das apps é: não permitir framing. Se você tiver um motivo real para incorporar, mantenha a permissão estreita.

Padrões práticos:

  • Sem embeds: frame-ancestors 'none'
  • Apenas seu site: frame-ancestors 'self'
  • Seu site mais um host confiável: frame-ancestors 'self' https://partner.example
  • Mantenha X-Frame-Options como backup: DENY ou SAMEORIGIN

Teste rápido: tente carregar uma página sensível dentro de um iframe em um arquivo HTML simples. Se ela continuar em branco, a proteção de framing está funcionando. Isso é comum em protótipos gerados por IA porque templates frequentemente esquecem esses cabeçalhos ou os aplicam de forma inconsistente entre rotas.

Passo a passo: aplicar cabeçalhos com risco mínimo

A forma mais segura de adicionar cabeçalhos de segurança é começar com uma linha de base que mude pouco, provar que nada quebrou e então apertar uma regra por vez. Trate isso como uma mudança de produto, não como um ajuste rápido de configuração.

1) Adicione uma linha de base e depois aperte

Uma ordem prática:

  • Defina proteção de framing primeiro (menos provável de quebrar scripts).
  • Adicione HSTS com cuidado (só depois do HTTPS estar sólido em todo lugar).
  • Adicione uma CSP básica que permita os scripts atuais, depois remova permissões inseguras gradualmente.
  • Reteste fluxos críticos (login, checkout, uploads, embeds) após cada mudança.
  • Registre exceções com razão e responsável.

2) Escolha um único lugar para setar cabeçalhos

Você pode definir cabeçalhos no servidor da app (Express, Rails, Django), num reverse proxy (Nginx) ou nas configurações da plataforma de hospedagem. Escolha uma fonte de verdade. Se os cabeçalhos estiverem definidos em vários lugares, você acabará enviando valores conflitantes e perdendo horas depurando o que o navegador realmente recebe.

3) Faça o rollout como um recurso

Aplique mudanças em staging primeiro, depois libere para uma fatia pequena de tráfego (ou um ambiente limitado) antes do rollout completo. Tenha uma opção de rollback rápida (uma mudança de configuração, não um redeploy) para recuperar rápido se um script de terceiro ou um redirect OAuth falhar.

4) Documente exceções para que não se espalhem

Quando você tiver que permitir algo (um host de script, um snippet inline, um iframe), registre o que isso habilita, por que é necessário e o que o substituirá depois. Caso contrário, exceções “temporárias” viram permanentes.

Se você herdou um protótipo gerado por IA, esses cabeçalhos frequentemente revelam problemas ocultos rapidamente. FixMyMess pode rodar uma auditoria rápida e ajudar a adicionar cabeçalhos com segurança sem quebrar comportamento em produção.

Como testar rapidamente (e entender o que quebrou)

Find hidden CSP blockers
Identify inline scripts, eval usage, and risky third-party tags before you enforce CSP.

Testes rápidos valem mais que adivinhação. Primeiro confirme que o navegador está recebendo os cabeçalhos. Depois veja o que o navegador bloqueou.

Verifique no DevTools primeiro

Abra seu site, então DevTools:

  • Network: clique na requisição do documento principal e verifique Response Headers por CSP, HSTS e framing.
  • Console: violações de CSP aparecem com mensagens como “Refused to load...” junto com a URL bloqueada e a diretiva (por exemplo, script-src).
  • Painel Security (na maioria dos navegadores): confirme o HTTPS e veja detalhes do certificado.
  • Painel Application: verifique se o HSTS está em efeito.

Quando algo quebra depois de adicionar CSP, a mensagem no console é seu mapa. Correlacione o item bloqueado com a diretiva:

  • Script ou código inline bloqueado -> script-src
  • Chamada de API bloqueada -> connect-src
  • Imagem/fonte bloqueada -> img-src ou font-src

Exemplo: se o login para de funcionar e o console diz que uma requisição para https://api.yourapp.com foi bloqueada por connect-src, corrija permitindo esse host em connect-src (não afrouxando tudo).

Verificações rápidas na linha de comando

Esses cheques confirmam redirects, HTTPS e que os cabeçalhos são realmente enviados pelo seu servidor (não apenas configurados em algum lugar que você está contornando).

curl -I http://yourdomain.com
curl -I https://yourdomain.com
curl -I -L https://yourdomain.com

Procure por:

  • Strict-Transport-Security apenas nas respostas HTTPS
  • a resposta final após redirects (com -L) ainda incluir seus cabeçalhos

Para confirmar que o framing bloqueia sem ferramentas especiais, crie um HTML mínimo que faça um iframe do seu site e abra localmente. Se ficar em branco ou der erro, sua política de framing está funcionando.

Se precisar de ajuda para interpretar logs de violações ou consertar um protótipo que quebrou após apertar cabeçalhos, FixMyMess pode auditar o código e aplicar correções seguras rápido.

Erros comuns que causam outages ou confiança falsa

A maioria dos problemas com cabeçalhos de segurança não são “problemas de segurança”. São problemas de rollout. Uma pequena mudança pode bloquear scripts, quebrar logins ou esconder problemas reais atrás de um workaround rápido.

Erros de CSP que quebram funcionalidades reais

A maneira mais rápida de derrubar um frontend é aplicar uma CSP estrita antes de inventariar o que a página realmente carrega. Scripts inline, manipuladores de evento inline (como onclick), widgets de terceiros e tags de analytics injetadas costumam existir mesmo em apps “simples”.

Armadilhas comuns:

  • Aplicar CSP rígida antes de inventariar scripts inline e fontes terceiras
  • Usar 'unsafe-inline' e curingas amplos para “fazer funcionar” e nunca apertar depois
  • Bloquear connect-src por acidente, fazendo chamadas API falharem e parecer que o backend está fora

Um padrão mais seguro é: comece com report-only, corrija os maiores culpados e depois passe a aplicar. Se você está consertando um protótipo, muitas vezes é mais rápido substituir scripts inline por arquivos JS reais do que continuar adicionando exceções.

Armadilhas de HSTS e framing

HSTS é ótimo até ser definido num domínio que ainda serve HTTP em algum lugar (subdomínios antigos, um painel admin esquecido, um host de staging). Depois que navegadores cacheiam HSTS, “voltar atrás” não resolve rápido para os usuários.

Proteção de framing também pode causar confusão. X-Frame-Options ajuda, mas o controle moderno costuma ser frame-ancestors no CSP. Se você incorpora sua app dentro de outro site (pagamentos, portais de parceiros, ferramentas internas), uma configuração estrita pode quebrar esse fluxo.

Mais um driver de outages: confundir CORS com CSP. Erros de CORS são sobre permitir leituras cross-site. Erros de CSP são sobre permitir o que a página pode carregar ou executar. Perseguir o problema errado consome horas.

Se você herdou código gerado por IA, essas questões se acumulam rápido. FixMyMess frequentemente vê equipes “consertando” sintomas desabilitando proteções em vez de remover scripts inline, limpar fontes e mover segredos para fora do cliente. Isso pode dar um checkbox verde, mas não endurece realmente a segurança.

Checklist rápido antes de enviar para produção

Antes de habilitar esses cabeçalhos em produção, faça uma checagem rápida para capturar a armadilha “funcionou numa página”. Cabeçalhos só ajudam se forem consistentes, e não devem surpreender usuários com logins quebrados, redirects travados ou páginas em branco.

  • Verifique tipos de resposta diferentes: páginas normais, respostas de API, redirects (301/302) e páginas de erro (404/500). Confirme que os cabeçalhos aparecem em todas.
  • Carregue sua app e confirme que não há avisos de conteúdo misto (imagens, scripts ou fontes HTTP numa página HTTPS).
  • Comece CSP em Report-Only, corrija itens barulhentos e só então aplique.
  • Bloqueie framing por padrão e permita apenas quando houver razão clara.
  • Habilite HSTS somente depois que HTTPS estiver estável em todo lugar, incluindo subdomínios que você controla e pontos de entrada comuns como páginas de marketing e callbacks.

Dois testes rápidos:

# Check headers on a normal page
curl -I https://yourdomain.com/

# Check headers on a redirect target too
curl -I -L https://yourdomain.com/login

No navegador, abra o Console do DevTools e faça refresh. Se a CSP estiver muito rígida, você geralmente verá mensagens claras sobre o que foi bloqueado.

Um problema real comum: a página inicial tem CSP, mas o redirect de login ou o handler de 404 é servido por uma camada diferente (CDN, default do framework), então os cabeçalhos desaparecem silenciosamente.

Se você herdou um protótipo gerado por IA, essas inconsistências são especialmente comuns. Uma auditoria rápida de algumas rotas chave pode evitar um lançamento “seguro” que só funciona no caminho feliz.

Exemplo: apertando cabeçalhos num protótipo sem quebrar tudo

Lock down framing correctly
Block clickjacking with frame-ancestors and fix any embed flows that need allowlists.

Você herda um protótipo gerado por IA (por exemplo do Bolt ou Replit). Funciona no demo, mas depende de scripts inline no HTML, um widget de chat de terceiro colado na página e algumas tags de rastreamento adicionadas de última hora.

Se você aplicar uma CSP rígida imediatamente, a app pode parecer “ok” até você testar fluxos reais. Quebras típicas: o redirect de login falha porque um script inline não rodou, o chat fica em branco porque seus scripts e frames foram bloqueados, e o analytics para de enviar eventos justamente quando você precisa.

Um plano de rollout que evita a maioria das surpresas:

  • Comece com Content-Security-Policy-Report-Only para que nada seja bloqueado ainda.
  • Use o console e relatórios CSP para listar o que a app realmente carrega.
  • Substitua scripts inline por arquivos externos quando possível, ou adicione nonces para os que precisa manter.
  • Adicione explicitamente os domínios do chat e analytics (apenas os que você usa).
  • Enforce a CSP somente depois que as jornadas principais do usuário (login, checkout, configurações) estiverem limpas.

Depois disso, seu conjunto de cabeçalhos pode permanecer simples em alto nível:

  • CSP: bloqueio padrão, depois permita seus próprios scripts mais uma allowlist curta para fornecedores necessários; use nonces para qualquer código inline restante.
  • HSTS: habilite com um max-age razoável depois que HTTPS estiver estável em todo lugar.
  • Framing: bloqueie incorporação (ou permita apenas sua própria origem) usando frame-ancestors.

Próximos passos: manter a segurança conforme a app muda

Cabeçalhos seguros não são “configurar e esquecer”. Cada nova rota, tag de analytics, widget de chat, script de pagamento ou mudança de CDN pode alterar o que o navegador precisa permitir.

Mantenha uma nota curta de “política de cabeçalhos” junto às suas notas de deploy: o que sua CSP deve permitir (scripts, estilos, frames), se HSTS está habilitado e se sua app pode ser enquadrada (geralmente não). Parece simples, mas evita edições de pânico quando algo quebra.

Revise seus cabeçalhos sempre que adicionar scripts de terceiros e considere uma verificação mensal rápida. A maioria das quebras vem de “apenas um snippet”.

Se sua app foi gerada por ferramentas como Lovable, Bolt, v0, Cursor ou Replit, espere scripts inline ocultos e padrões inseguros. Esses casos frequentemente pressionam equipes a permitir scripts inline na CSP. Encare isso como um cheiro de código: conserte o código para poder apertar a política.

Se quiser uma segunda opinião, FixMyMess (fixmymess.ai) oferece uma auditoria de código gratuita para apps geradas por IA. É uma maneira rápida de apontar os problemas de cabeçalhos que vão quebrar produção e depois consertar o código subjacente (auth, segredos, scripts confusos) para que as configurações de segurança possam ser aplicadas em segurança.

Perguntas Frequentes

Which security header should I add first if I don’t want to break my app?

Start with CSP in Report-Only, then add framing protection, and add HSTS last. CSP is the one most likely to break scripts and auth flows, so you want visibility before you enforce anything.

Why did my login or checkout stop working right after I enabled CSP?

A strict CSP often blocks inline scripts, third-party tags, or auth/payment widgets that load from domains you didn’t allow. The fix is usually to read the console violation, then allow the specific host in the right directive (often script-src or connect-src) instead of loosening everything.

What’s the safest way to roll out a new CSP?

Use Content-Security-Policy-Report-Only first. It shows you what would be blocked without actually blocking it, so you can collect violations and update your policy safely before enforcing.

Should I use nonces or hashes for CSP, and when?

Avoid 'unsafe-inline' as a long-term setting. Prefer nonces for inline scripts that are generated at request time, or hashes for small, stable inline snippets, so you can keep CSP strict without breaking the UI.

How do I enable HSTS without locking users out if something goes wrong?

Start with a small max-age (like an hour or a day) and increase it after multiple clean deploys. Only enable HSTS once HTTPS redirects, certificates, and mixed content are all stable, because browsers can cache HSTS and you can’t quickly undo it for users.

What’s risky about using HSTS with includeSubDomains or preload?

includeSubDomains forces HTTPS on every subdomain, including forgotten ones like old admin panels or legacy marketing hosts. preload is an even bigger commitment; it’s best reserved for mature domains where HTTPS will never be removed.

Should I block iframes with X-Frame-Options or CSP frame-ancestors?

Default to blocking framing unless you have a clear embed use case. Use frame-ancestors in CSP for modern control, and optionally keep X-Frame-Options: DENY or SAMEORIGIN as a backup for older clients.

How can I quickly confirm my headers are actually being sent and enforced?

Open DevTools and check the Network tab for response headers, then read the Console for CSP errors like “Refused to load…” that name the blocked URL and directive. Also run curl -I (and curl -I -L) to confirm the final response after redirects still includes your headers.

How do I tell the difference between a CSP problem and a CORS problem?

CSP controls what the page is allowed to load and run (scripts, connections, frames). CORS controls whether a browser allows one site to read responses from another site; mixing them up leads to the wrong fix and a lot of wasted time.

Why do AI-generated prototypes break more often when I add security headers, and what should I do?

AI-generated apps often rely on inline scripts, hidden eval() paths, and copy-pasted third-party snippets that work in preview but fail under real domains and strict headers. If you need help tightening headers without breaking production, FixMyMess can run a free code audit and fix the underlying code so CSP, HSTS, and framing rules can be enforced safely.