Scripting em vários locais (XSS) a capacidade de injetar scripts maliciosos em um aplicativo da Web, tem sido uma das maiores vulnerabilidades de segurança da Web há mais de uma década.
Política de Segurança de Conteúdo (CSP)
é uma camada extra de segurança que ajuda a mitigar ataques de XSS. Para configurar um CSP,
adicione o cabeçalho HTTP Content-Security-Policy
a uma página da Web e defina valores
controlar quais recursos o user agent pode carregar para essa página.
Esta página explica como usar uma CSP baseada em valores de uso único ou hashes para mitigar XSS, em vez dos CSPs com base em lista de permissões de hosts usados com frequência que costumam sair da página expostos a XSS porque podem ser ignorados na maioria das configurações.
Termo-chave: um valor de uso único é um número aleatório usado apenas uma vez que você pode usar para marcar um
tag <script>
como confiável.
Termo-chave: uma função hash é uma função matemática que converte uma entrada
em um valor numérico compactado chamado hash. É possível usar um hash
(por exemplo, SHA-256) para marcar um
tag <script>
como confiável.
Uma Política de Segurança de Conteúdo baseada em valores de uso único ou hashes geralmente é chamada de CSP rigorosa. Quando um aplicativo usa uma CSP rigorosa, os invasores que encontram HTML as falhas de injeção geralmente não podem usá-las para forçar a execução do navegador scripts maliciosos em um documento vulnerável. Isso ocorre porque apenas CSPs rigorosos permite scripts em hash ou scripts com o valor de uso único correto gerado no de modo que os invasores não podem executar o script sem saber o valor de uso único correto. para uma determinada resposta.
Por que você deve usar uma CSP rigorosa?
Caso seu site já tenha uma CSP parecida com script-src www.googleapis.com
,
ele provavelmente não é eficaz contra sites cruzados. Esse tipo de CSP é chamado de
CSP da lista de permissões. Eles exigem muita personalização e podem ser
ignorados por invasores.
Os CSPs rígidos baseados em valores de uso único ou hashes criptográficos evitam esses problemas.
Estrutura rígida de CSP
Uma Política básica de Segurança de Conteúdo rigorosa usa uma das seguintes respostas HTTP cabeçalhos:
CSP rigorosa que não se baseia noce CE
Content-Security-Policy:
script-src 'nonce-{RANDOM}' 'strict-dynamic';
object-src 'none';
base-uri 'none';
CSP rigorosa baseada em hash
Content-Security-Policy:
script-src 'sha256-{HASHED_INLINE_SCRIPT}' 'strict-dynamic';
object-src 'none';
base-uri 'none';
As propriedades a seguir tornam uma CSP como esta "restrita" e, portanto, seguros:
- Ela usa valores de uso único
'nonce-{RANDOM}'
ou hashes'sha256-{HASHED_INLINE_SCRIPT}'
para indicar em quais tags<script>
o desenvolvedor do site confia para executar no navegador do usuário. - Ele define
'strict-dynamic'
. para reduzir o esforço de implantação de um CSP com base em hash ou valor de uso único. a execução de scripts criados por um script confiável. Isso também desbloqueia o uso da maioria das bibliotecas e dos widgets JavaScript de terceiros. - Ele não é baseado em listas de permissões de URL e, por isso, não sofre ignorações comuns da CSP.
- Bloqueia scripts inline não confiáveis, como manipuladores de eventos inline ou
javascript:
URIs. - Ele restringe o
object-src
a desativar plug-ins perigosos como o Flash. - Ele restringe o
base-uri
para bloquear a injeção de tags<base>
. Isso evita os invasores de mudar a localização dos scripts carregados de URLs relativos.
Adote uma CSP rigorosa
Para adotar uma CSP rigorosa, você precisa:
- Decida se o aplicativo deve definir um CSP com base em hash ou valor de uso único.
- Copie o CSP da seção Estrutura estrita de CSP e configure-o como um cabeçalho de resposta em todo o aplicativo.
- Refatore modelos HTML e o código do lado do cliente para remover padrões que estejam incompatíveis com a CSP.
- Implante seu CSP.
Você pode usar o Lighthouse.
(v7.3.0 e mais recentes com a flag --preset=experimental
) Auditoria de Práticas recomendadas
ao longo desse processo para verificar se o site tem uma CSP e se está
rígidas o suficiente para serem eficazes contra XSS.
Etapa 1: decidir se você precisa de uma CSP com base em valor de uso único ou em hash
Confira como funcionam os dois tipos de CSP rigorosa:
CSP baseado em valor de uso único
Com um CSP baseado em valor de uso único, você gera um número aleatório durante a execução, inclui-o em sua CSP e associá-la a cada tag de script na sua página. Um invasor não podem incluir ou executar um script malicioso em sua página, porque precisam adivinhar o número aleatório correto para aquele script. Isso só funciona se o número não pode ser adivinhado e é gerado no momento da execução para cada resposta.
Use uma CSP com base em valor de uso único para páginas HTML renderizadas no servidor. Para essas páginas, é possível criar um novo número aleatório para cada resposta.
CSP baseado em hash
Para uma CSP baseada em hash, o hash de cada tag de script in-line é adicionado à CSP. Cada script tem um hash diferente. Um invasor não pode incluir ou executar uma script em sua página, pois o hash desse script precisaria estar em seu e o CSP para que ele seja executado.
Use um CSP baseado em hash para páginas HTML veiculadas estaticamente ou páginas que precisam ser armazenadas em cache. Por exemplo, é possível usar um CSP baseado em hash para aplicativos da Web aplicativos criados com frameworks como Angular, React ou outros, que são exibidos estaticamente sem renderização do lado do servidor.
Etapa 2: definir uma CSP rigorosa e preparar seus scripts
Ao definir uma CSP, você tem algumas opções:
- Modo somente relatórios (
Content-Security-Policy-Report-Only
) ou modo de aplicação (Content-Security-Policy
). No modo somente relatórios, a CSP não bloqueia recursos ainda, então nada em seu site falha, mas você pode ver erros e obter relatórios sobre tudo que seria bloqueado. Localmente, quando você está configurar sua CSP, isso não importa, porque os dois modos mostram a você no console do navegador. Se for o caso, o modo de aplicação ajuda a encontrar recursos que o rascunho da CSP bloqueia, porque o bloqueio de um recurso pode tornar a página parecer corrompida. O modo somente relatório se torna mais útil posteriormente no processo Consulte a Etapa 5. - Cabeçalho ou tag HTML
<meta>
. Para desenvolvimento local, uma tag<meta>
pode ser mais conveniente para ajustar sua CSP e ver rapidamente como ela afeta seu site. No entanto:- Depois, ao implantar seu CSP na produção, recomendamos defini-lo como um cabeçalho HTTP.
- Se você quiser definir sua CSP no modo somente relatório, será necessário defini-la como uma porque as metatags CSP não são compatíveis com o modo somente relatório.
Defina a seguinte resposta HTTP Content-Security-Policy
cabeçalho em seu aplicativo:
Content-Security-Policy: script-src 'nonce-{RANDOM}' 'strict-dynamic'; object-src 'none'; base-uri 'none';
Gerar um valor de uso único para a CSP
Um valor de uso único é um número aleatório usado apenas uma vez por carregamento de página. Um valor de uso único A CSP só pode mitigar o XSS se os invasores não conseguirem adivinhar o valor de uso único. Um O valor de uso único da CSP precisa ser:
- Um valor aleatório com criptografia forte (de preferência, com mais de 128 bits de comprimento)
- Gerado recentemente para cada resposta
- Codificação em Base64
Confira alguns exemplos de como adicionar um valor de uso único da CSP em frameworks do lado do servidor:
- Django (python)
- Expresso (JavaScript):
const app = express(); app.get('/', function(request, response) { // Generate a new random nonce value for every response. const nonce = crypto.randomBytes(16).toString("base64"); // Set the strict nonce-based CSP response header const csp = `script-src 'nonce-${nonce}' 'strict-dynamic'; object-src 'none'; base-uri 'none';`; response.set("Content-Security-Policy", csp); // Every <script> tag in your application should set the `nonce` attribute to this value. response.render(template, { nonce: nonce }); });
Adicionar um atributo nonce
aos elementos <script>
Com uma CSP baseada em valor de uso único, cada elemento <script>
precisa
ter um atributo nonce
que corresponda ao valor de uso único aleatório.
especificado no cabeçalho CSP. Todos os scripts podem ter a mesma
valor de uso único. A primeira etapa é adicionar esses atributos a todos os scripts para que
o CSP permite que elas.
Defina a seguinte resposta HTTP Content-Security-Policy
cabeçalho em seu aplicativo:
Content-Security-Policy: script-src 'sha256-{HASHED_INLINE_SCRIPT}' 'strict-dynamic'; object-src 'none'; base-uri 'none';
Para vários scripts inline, a sintaxe é a seguinte:
'sha256-{HASHED_INLINE_SCRIPT_1}' 'sha256-{HASHED_INLINE_SCRIPT_2}'
:
Carregar scripts de origem dinamicamente
Como os hashes de CSP só têm suporte para scripts inline nos navegadores, é preciso carregar todos os scripts de terceiros dinamicamente usando um script in-line. Os hashes de scripts originados não têm suporte em todos os navegadores.
<script> var scripts = [ 'https://example.org/foo.js', 'https://example.org/bar.js']; scripts.forEach(function(scriptUrl) { var s = document.createElement('script'); s.src = scriptUrl; s.async = false; // to preserve execution order document.head.appendChild(s); }); </script>
<script src="https://example.org/foo.js"></script> <script src="https://example.org/bar.js"></script>
Considerações sobre o carregamento do script
O exemplo de script in-line adiciona s.async = false
para garantir
que foo
executa antes de bar
, mesmo que
bar
é carregado primeiro. Neste snippet, s.async = false
não bloqueia o analisador enquanto os scripts são carregados, porque eles são
adicionados dinamicamente. O analisador para apenas quando os scripts são executados, pois
o que faria para scripts async
. No entanto, com esse snippet,
lembre-se:
-
Um ou ambos os scripts podem ser executados antes da conclusão do documento
o download. Se você quiser que o documento esteja pronto quando o
scripts forem executados, aguarde o evento
DOMContentLoaded
antes você anexa os scripts. Se isso causar um problema de desempenho porque os scripts não iniciarem o download com antecedência suficiente, use as tags de pré-carregamento anteriormente na página. -
O
defer = true
não faz nada. Se você precisar comportamento, execute o script manualmente quando necessário.
Etapa 3: refatorar os modelos HTML e o código do lado do cliente
Manipuladores de eventos inline (como onclick="…"
, onerror="…"
) e URIs JavaScript
(<a href="javascript:…">
) pode ser usada para executar scripts. Isso significa que
o invasor que encontra um bug XSS poderá injetar esse tipo de HTML e executar
JavaScript. Uma CSP com base em hash ou valor de uso único proíbe o uso desse tipo de marcação.
Se o site usar algum desses padrões, será necessário refatorá-los em configurações mais seguras
alternativas.
Se você ativou a CSP na etapa anterior, pode conferir as violações de CSP em console toda vez que o CSP bloqueia um padrão incompatível.
Na maioria dos casos, a correção é simples:
Refatorar manipuladores de eventos inline
<span id="things">A thing.</span> <script nonce="${nonce}"> document.getElementById('things').addEventListener('click', doThings); </script>
<span onclick="doThings();">A thing.</span>
Refatorar URIs javascript:
<a id="foo">foo</a> <script nonce="${nonce}"> document.getElementById('foo').addEventListener('click', linkClicked); </script>
<a href="javascript:linkClicked()">foo</a>
Remover eval()
do JavaScript
Se o aplicativo usa eval()
para converter serializações de string JSON em JS
objetos, refatore essas instâncias para JSON.parse()
, que também é
mais rápido.
Se não for possível remover todos os usos de eval()
, defina uma configuração com base em valor de uso único
CSP, mas você precisa usar a palavra-chave 'unsafe-eval'
da CSP, o que facilita
uma política um pouco menos segura.
É possível encontrar esses e outros exemplos de refatoração nesta CSP rigorosa codelab:
Etapa 4 (opcional): adicionar substitutos para oferecer suporte a versões antigas do navegador
Se você precisar de suporte para versões mais antigas do navegador:
- O uso de
strict-dynamic
requer a adição dehttps:
como substituto para uma e outras versões do Safari. Ao fazer isso:- Todos os navegadores compatíveis com
strict-dynamic
ignoram o substitutohttps:
. portanto, isso não reduz a força da política. - Em navegadores antigos, os scripts de origem externa só podem ser carregados se vierem de
uma origem HTTPS. É menos seguro do que uma CSP rigorosa, mas ainda assim
impede algumas causas comuns de XSS, como injeções de URIs
javascript:
.
- Todos os navegadores compatíveis com
- Para garantir a compatibilidade com versões muito antigas dos navegadores (mais de 4 anos), você pode adicionar
unsafe-inline
como substituto. Todos os navegadores recentes ignoramunsafe-inline
se houver um valor de uso único ou hash da CSP.
Content-Security-Policy:
script-src 'nonce-{random}' 'strict-dynamic' https: 'unsafe-inline';
object-src 'none';
base-uri 'none';
Etapa 5: implantar o CSP
Depois de confirmar que sua CSP não bloqueia scripts legítimos na sua ambiente de desenvolvimento local, é possível implantar seu CSP no preparo e, em seguida, no ambiente de produção:
- (Opcional) Implante seu CSP no modo somente relatório usando o
Cabeçalho
Content-Security-Policy-Report-Only
. O modo somente relatório é útil para testar uma alteração potencialmente interruptiva, como um novo CSP em produção, antes de e começar a aplicar restrições da CSP. No modo somente relatório, sua CSP não afetam o comportamento do aplicativo, mas o navegador ainda gera erros do console e violações de políticas ao encontrar padrões incompatíveis com sua CSP, para que você possa ver o que pode prejudicar os usuários finais. Para mais informações, consulte API Reporting. - Quando você tiver certeza de que o CSP não prejudicará seu site para os usuários finais,
implantar o CSP usando o cabeçalho de resposta
Content-Security-Policy
. Qa recomendamos configurar sua CSP usando um cabeçalho HTTP no lado do servidor, porque é mais mais segura que uma tag<meta>
. Depois que você concluir essa etapa, seu CSP vai começar protegendo seu app de XSS.
Limitações
Um CSP estrito geralmente fornece uma forte camada adicional de segurança que ajuda a
mitigar ataques de XSS. Na maioria dos casos, o CSP reduz significativamente a superfície de ataque,
rejeitar padrões perigosos, como URIs javascript:
. No entanto, com base no tipo
de CSP que você usa (valores de uso único, hashes, com ou sem 'strict-dynamic'
), há
são casos em que a CSP não protege o app também:
- Se você usar um script com valor de uso único, mas houver uma injeção diretamente no corpo ou no
parâmetro
src
desse elemento<script>
. - Se houver injeções nos locais dos scripts criados dinamicamente
(
document.createElement('script')
), inclusive em qualquer função da biblioteca que criam nós DOMscript
com base nos valores dos argumentos. Isso inclui algumas APIs comuns, como.html()
do jQuery, bem como.get()
e.post()
em jQuery < 3,0. - Se houver injeções de modelo em aplicativos antigos do AngularJS. Um invasor que pode injetar em um modelo AngularJS pode usá-lo para executar JavaScript arbitrário.
- Se a política contiver
'unsafe-eval'
, injeções emeval()
,setTimeout()
e algumas outras APIs raramente usadas.
Os desenvolvedores e engenheiros de segurança devem prestar atenção especial a essas em revisões de código e auditorias de segurança. Você pode encontrar mais detalhes esses casos na Política de Segurança de Conteúdo: uma confusão bem-sucedida entre proteção e mitigação (em inglês).
Leitura adicional
- A CSP está morta, vida longa ao CSP! Sobre a insegurança das listas de permissões e o futuro da política de segurança de conteúdo
- Avaliador da CSP
- Conferência LocaloMoco: política de segurança de conteúdo — uma confusão bem-sucedida entre proteção e mitigação
- Palestra do Google I/O: Como proteger apps da Web com recursos modernos de plataforma (em inglês)