Isolamento de sites para desenvolvedores da Web

O Chrome 67 para computador tem um novo recurso chamado isolamento de sites ativado por padrão. Este artigo explica o que é o isolamento do site, por que ele é necessário e por que os desenvolvedores da Web devem estar cientes dele.

O que é o isolamento de sites?

A Internet é para assistir vídeos de gatos e gerenciar carteiras de criptomoedas, entre outras coisas. Mas você não quer que o fluffycats.example tenha acesso às suas criptomoedas. Felizmente, os sites geralmente não podem acessar os dados uns dos outros no navegador, graças à política de mesma origem. Ainda assim, sites maliciosos podem tentar contornar essa política para atacar outros sites. Além disso, às vezes, bugs de segurança são encontrados no código do navegador que aplica a política de mesma origem. A equipe do Chrome tem como objetivo corrigir esses bugs o mais rápido possível.

O isolamento de sites é um recurso de segurança no Chrome que oferece uma linha de defesa extra para reduzir a probabilidade de sucesso desses ataques. Ele garante que as páginas de sites diferentes sejam sempre colocadas em processos diferentes, cada um executado em um sandbox que limita o que o processo pode fazer. Ele também impede que o processo receba determinados tipos de dados sensíveis de outros sites. Como resultado, com o isolamento de site, é muito mais difícil para um site malicioso usar ataques especulativos de canal lateral, como Spectre, para roubar dados de outros sites. À medida que a equipe do Chrome terminar outras implementações, o isolamento de site também ajudará mesmo quando a página de um invasor puder quebrar algumas das regras no próprio processo.

O isolamento de sites dificulta o acesso ou roubo de informações de suas contas em outros sites por sites não confiáveis. Ele oferece proteção extra contra vários tipos de bugs de segurança, como os ataques de canal lateral Meltdown e Spectre.

Para mais detalhes sobre a isolação de sites, consulte nosso artigo no blog de segurança do Google.

Bloqueio de leitura entre origens

Mesmo quando todas as páginas entre sites são colocadas em processos separados, elas ainda podem solicitar legitimamente alguns subrecursos entre sites, como imagens e JavaScript. Uma página da Web maliciosa pode usar um elemento <img> para carregar um arquivo JSON com dados sensíveis, como seu saldo bancário:

<img src="https://your-bank.example/balance.json" />
<!-- Note: the attacker refused to add an `alt` attribute, for extra evil points. -->

Sem o isolamento de site, o conteúdo do arquivo JSON seria enviado para a memória do processo de renderização, e o renderizador perceberia que não é um formato de imagem válido e não renderizaria uma imagem. No entanto, o invasor pode explorar uma vulnerabilidade como Spectre para ler esse pedaço de memória.

Em vez de usar <img>, o invasor também pode usar <script> para comprometer os dados sensíveis na memória:

<script src="https://your-bank.example/balance.json"></script>

O bloqueio de leitura de origem cruzada (CORB, na sigla em inglês) é um novo recurso de segurança que impede que o conteúdo de balance.json entre na memória do processo do renderizador com base no tipo MIME.

Vamos explicar como o CORB funciona. Um site pode solicitar dois tipos de recursos de um servidor:

  1. recursos de dados, como documentos HTML, XML ou JSON
  2. Recursos de mídia, como imagens, JavaScript, CSS ou fontes

Um site pode receber recursos de dados da própria origem ou de outras origens com cabeçalhos CORS permissivos, como Access-Control-Allow-Origin: *. Por outro lado, os recursos de mídia podem ser incluídos de qualquer origem, mesmo sem cabeçalhos CORS permissivos.

O CORB impede que o processo do renderizador receba um recurso de dados entre origens (por exemplo, HTML, XML ou JSON) se:

  • o recurso tem um cabeçalho X-Content-Type-Options: nosniff
  • O CORS não permite explicitamente o acesso ao recurso

Se o recurso de dados entre origens não tiver o cabeçalho X-Content-Type-Options: nosniff definido, o CORB vai tentar detectar o corpo da resposta para determinar se ele é HTML, XML ou JSON. Isso é necessário porque alguns servidores da Web estão configurados incorretamente e veiculam imagens como text/html, por exemplo.

Os recursos de dados bloqueados pela política CORB são apresentados ao processo como vazios, embora a solicitação ainda aconteça em segundo plano. Como resultado, uma página da Web maliciosa tem dificuldade para extrair dados de vários sites no processo de roubo.

Para ter a melhor segurança e aproveitar os benefícios do CORB, recomendamos o seguinte:

  • Marque as respostas com o cabeçalho Content-Type correto. Por exemplo, os recursos HTML precisam ser servidos como text/html, os recursos JSON com um tipo MIME JSON e os recursos XML com um tipo MIME XML.
  • Desative o sniffing usando o cabeçalho X-Content-Type-Options: nosniff. Sem esse cabeçalho, o Chrome faz uma análise rápida do conteúdo para tentar confirmar se o tipo está correto. No entanto, como ele permite respostas para evitar o bloqueio de itens como arquivos JavaScript, é melhor fazer a coisa certa por conta própria.

Para mais detalhes, consulte o artigo sobre CORB para desenvolvedores da Web ou nossa explicação detalhada sobre CORB.

Por que os desenvolvedores da Web devem se preocupar com o isolamento de sites?

Na maioria das vezes, o isolamento de site é um recurso do navegador que não é exposto diretamente aos desenvolvedores da Web. Não há uma nova API exposta na Web para aprender, por exemplo. Em geral, as páginas da Web não podem identificar a diferença ao serem executadas com ou sem o isolamento de site.

No entanto, há algumas exceções a essa regra. Ativar o isolamento de site tem alguns efeitos colaterais sutis que podem afetar seu site. Mantemos uma lista de problemas conhecidos de isolamento de site e detalhamos os mais importantes abaixo.

O layout de página inteira não é mais síncrono

Com o isolamento de site, o layout de página inteira não é mais garantido como síncrono, já que os frames de uma página podem ser distribuídos em vários processos. Isso pode afetar as páginas se elas presumirem que uma mudança de layout se propaga imediatamente para todos os frames na página.

Como exemplo, vamos considerar um site chamado fluffykittens.example que se comunica com um widget social hospedado em social-widget.example:

<!-- https://fluffykittens.example/ -->
<iframe src="https://social-widget.example/" width="123"></iframe>
<script>
  const iframe = document.querySelector('iframe');
  iframe.width = 456;
  iframe.contentWindow.postMessage(
    // The message to send:
    'Meow!',
    // The target origin:
    'https://social-widget.example'
  );
</script>

No início, a largura do <iframe> do widget social é de 123 pixels. No entanto, a página FluffyKittens muda a largura para 456 pixels (acionando o layout) e envia uma mensagem para o widget social, que tem o seguinte código:

<!-- https://social-widget.example/ -->
<script>
  self.onmessage = () => {
    console.log(document.documentElement.clientWidth);
  };
</script>

Sempre que o widget social recebe uma mensagem pela API postMessage, ele registra a largura do elemento raiz <html>.

Qual valor de largura é registrado? Antes do Chrome ativar o isolamento de sites, a resposta era 456. O acesso a document.documentElement.clientWidth força o layout, que costumava ser síncrono antes do Chrome ativar o isolamento de sites. No entanto, com o isolamento de sites ativado, o redimensionamento do widget social entre origens agora acontece de forma assíncrona em um processo separado. Assim, a resposta também pode ser 123, ou seja, o valor antigo de width.

Se uma página mudar o tamanho de um <iframe> entre origens e enviar um postMessage para ele, com isolamento de site, o frame de recebimento talvez ainda não saiba o novo tamanho ao receber a mensagem. De modo mais geral, isso pode quebrar páginas se elas presumirem que uma mudança de layout se propaga imediatamente para todos os frames na página.

Neste exemplo específico, uma solução mais robusta definiria o width no frame pai e detectaria essa mudança no <iframe> detectando um evento resize.

Os manipuladores de descarregamento podem expirar com mais frequência

Quando um frame navega ou é fechado, o documento antigo e todos os documentos de subframe incorporados a ele executam o manipulador unload. Se a nova navegação acontecer no mesmo processo de renderizador (por exemplo, para uma navegação de mesma origem), os manipuladores unload do documento antigo e os subframes dele podem ser executados por um tempo arbitrariamente longo antes de permitir que a nova navegação seja confirmada.

addEventListener('unload', () => {
  doSomethingThatMightTakeALongTime();
});

Nessa situação, os gerenciadores unload em todos os frames são muito confiáveis.

No entanto, mesmo sem o isolamento do site, algumas navegações de frame principal são entre processos, o que afeta o comportamento do gerenciador de desligamento. Por exemplo, se você navegar de old.example para new.example digitando o URL na barra de endereço, a navegação new.example vai acontecer em um novo processo. Os gerenciadores de descarga para old.example e os subframes são executados no processo old.example em segundo plano, depois que a página new.example é mostrada, e os gerenciadores de descarga antigos são encerrados se não forem concluídos em um determinado tempo limite. Como os gerenciadores de remoção podem não ser concluídos antes do tempo limite, o comportamento de remoção é menos confiável.

Com o isolamento de sites, todas as navegações entre sites se tornam processos cruzados, para que documentos de sites diferentes não compartilhem um processo entre si. Como resultado, a situação acima se aplica em mais casos, e os gerenciadores de descarregamento em <iframe>s geralmente têm os comportamentos de segundo plano e de tempo limite descritos acima.

Outra diferença resultante do isolamento de site é a nova ordenação paralela de gerenciadores de remoção: sem o isolamento de site, os gerenciadores de remoção são executados em uma ordem rígida de cima para baixo em todos os frames. No entanto, com o isolamento de sites, os gerenciadores de descarregamento são executados em paralelo em diferentes processos.

Essas são consequências fundamentais da ativação do isolamento do site. A equipe do Chrome está trabalhando para melhorar a confiabilidade dos gerenciadores de desligamento para casos de uso comuns, quando possível. Também conhecemos bugs em que os gerenciadores de remoção de subframes ainda não conseguem usar determinados recursos e estamos trabalhando para resolvê-los.

Um caso importante para os gerenciadores de desligamento é enviar pings de fim de sessão. Isso geralmente é feito da seguinte maneira:

addEventListener('pagehide', () => {
  const image = new Image();
  img.src = '/end-of-session';
});

Uma abordagem melhor e mais robusta em relação a essa mudança é usar navigator.sendBeacon:

addEventListener('pagehide', () => {
  navigator.sendBeacon('/end-of-session');
});

Se você precisar de mais controle sobre a solicitação, use a opção keepalive da API Fetch:

addEventListener('pagehide', () => {
  fetch('/end-of-session', {keepalive: true});
});

Conclusão

O isolamento de sites dificulta o acesso ou roubo de informações de suas contas em outros sites por sites não confiáveis, isolando cada site no próprio processo. Como parte disso, o CORB tenta manter os recursos de dados sensíveis fora do processo de renderização. Nossas recomendações acima garantem que você aproveite ao máximo esses novos recursos de segurança.

Agradecemos a Alex Moshchuk, Charlie Reis, Jason Miller, Nasko Oskov, Philip Walton, Shubhie Panicker e Thomas Steiner por lerem uma versão preliminar deste artigo e enviarem feedback.