Pular para o conteúdo
DM Danilo Moura Desenvolvimento · Segurança da informação · LGPD
Araxá/MG · atende em 260 km Falar comigo

Início / Blog / Segurança

Segurança 20/08/2026 · 9 min de leitura

Fechar a CSP de verdade: o que quebra e como consertar

Quase toda Content-Security-Policy na internet tem unsafe-inline, o que anula boa parte do motivo dela existir. Tirar dá trabalho — e o trabalho é bem definido.

Content-Security-Policy é a diferença entre "alguém conseguiu injetar HTML na minha página" e "alguém conseguiu executar código no navegador dos meus visitantes". A primeira é um bug; a segunda é um incidente.

O problema é que a maioria das políticas na internet se parece com isto:

Content-Security-Policy: default-src 'self';
  script-src 'self' 'unsafe-inline' 'unsafe-eval';
  style-src 'self' 'unsafe-inline'

Com 'unsafe-inline' em script-src, a política deixa de impedir o ataque principal que ela deveria impedir. Um script injetado na página executa normalmente. Sobra proteção contra script de domínio externo — útil, mas é a menor parte.

Por que o unsafe-inline aparece

Ele aparece porque o site tem coisas assim, e tirar dá trabalho:

<div style="margin-top: 24px">
<button onclick="enviar()">
<script>var config = {...}</script>

Atributo style, atributo onclick e script embutido são todos bloqueados por uma política fechada. Quem descobre isso no meio de um deploy adiciona 'unsafe-inline', o site volta a funcionar, e a política vira decoração.

O que fazer com cada caso

1. Atributos style

Bloqueado

<p style="color:#999">

Funciona

<p class="t2">

Classes utilitárias resolvem. É trabalho mecânico e chato — em um site de porte médio dá algumas centenas de ocorrências — mas não é difícil, e um grep encontra todas.

2. Manipuladores inline

Bloqueado

<button onclick="enviar()">

Funciona

elemento.addEventListener(
  'click', enviar
);

3. Script embutido com dados do servidor

O caso legítimo mais comum: passar configuração do PHP para o JavaScript. A saída não é liberar script inline — é entregar por atributo de dado e ler no JS:

<div id="app" data-limite="50"></div>

// no JavaScript
var limite = +document.getElementById('app').dataset.limite;

A pegadinha do CSSOM

Um detalhe que confunde: com style-src 'self', o atributo style no HTML é bloqueado, mas o JavaScript continua podendo mexer em estilo:

el.style.setProperty('--x', '0.5');   // funciona
<div style="--x: 0.5">                // bloqueado

A política restringe estilo vindo do documento, não a manipulação via CSSOM. Efeito visual dinâmico feito em JavaScript continua funcionando com a política fechada.

Um degrau acima: Trusted Types

Fechado o unsafe-inline, sobra o XSS baseado em DOM — quando o próprio código do site escreve dado não confiável em um sink perigoso:

caixa.innerHTML = '<p>' + textoDoUsuario + '</p>';

Nenhuma CSP tradicional impede isso, porque o script é seu e está autorizado. A diretiva require-trusted-types-for 'script' bloqueia a atribuição direta a esses sinks.

Ela só pode ser ligada depois que o código não usa mais innerHTML. Construir os nós com createElement e textContent resolve — e como bônus elimina o bug de escape que ninguém tinha notado.

Ordem importa: primeiro tire o innerHTML, depois ligue a diretiva. O contrário quebra o site no Chrome e não avisa direito o porquê.

O risco depois da vitória

Fechada a política, aparece uma armadilha nova: quem escrever style="..." por hábito vê o estilo simplesmente não aplicar. Parece bug de CSS, não bloqueio de segurança — e a reação natural é afrouxar a política para "resolver".

Vale automatizar a defesa: um teste que falha se aparecer style= no HTML, e outro que falha se 'unsafe-inline' voltar ao cabeçalho. O comentário no código não impede o commit; o teste impede.

CTA próximo passo

Me conte o que está travando hoje

Uma conversa de vinte minutos costuma ser suficiente para separar o que é problema de sistema, o que é problema de processo e o que é risco de conformidade. Sem custo e sem compromisso.