Hospede com segurança dados do usuário em aplicativos da Web modernos

David Dworken
David Dworken

Muitos aplicativos da Web precisam mostrar conteúdo controlado pelo usuário. Isso pode ser tão simples quanto exibir imagens enviadas pelo usuário (por exemplo, fotos de perfil) ou tão complexo quanto renderizar HTML controlado pelo usuário (por exemplo, um tutorial de desenvolvimento da Web). Isso sempre foi difícil de fazer com segurança. Por isso, trabalhamos para encontrar soluções fáceis, mas seguras, que possam ser aplicadas à maioria dos tipos de aplicativos da Web.

Soluções clássicas para isolar conteúdo não confiável

A solução clássica para veicular conteúdo controlado pelo usuário com segurança é usar os chamados domínios de sandbox. A ideia básica é que, se o domínio principal do seu app for example.com, você poderá veicular todo o conteúdo não confiável em exampleusercontent.com. Como esses dois domínios são entre sites, qualquer conteúdo malicioso em exampleusercontent.com não pode afetar example.com.
Essa abordagem pode ser usada para oferecer com segurança todos os tipos de conteúdo não confiável, incluindo imagens, downloads e HTML. Embora não pareça necessário usar isso para imagens ou downloads, isso ajuda a evitar os riscos do sniffing de conteúdo, especialmente em navegadores legados.
Os domínios de sandbox são amplamente usados no setor e funcionaram bem por muito tempo. No entanto, elas têm duas desvantagens principais:

  • Os aplicativos geralmente precisam restringir o acesso ao conteúdo a um único usuário, o que exige a implementação de autenticação e autorização. Como os domínios de sandbox não compartilham cookies com o domínio principal do aplicativo, é muito difícil fazer isso com segurança. Para oferecer suporte à autenticação, os sites precisam usar URLs de recursos ou definir cookies de autenticação separados para o domínio do sandbox. Esse segundo método é especialmente problemático na Web moderna, em que muitos navegadores restringem cookies entre sites por padrão.
  • Embora o conteúdo do usuário seja isolado do site principal, ele não é isolado do conteúdo de outros usuários. Isso cria o risco de conteúdo malicioso do usuário atacar outros dados no domínio do sandbox, por exemplo, lendo dados de mesma origem.

Os domínios de sandbox também ajudam a reduzir os riscos de phishing, já que os recursos são segmentados de forma clara em um domínio isolado.

Soluções modernas para veicular conteúdo do usuário

Com o tempo, a Web evoluiu, e agora existem maneiras mais fáceis e seguras de exibir conteúdo não confiável. Há muitas abordagens diferentes, então vamos descrever duas soluções que são amplamente usadas no Google.

Abordagem 1: veicular conteúdo de usuários inativos

Se um site só precisa veicular conteúdo inativo do usuário (ou seja, conteúdo que não é HTML ou JavaScript, por exemplo, imagens e downloads), isso pode ser feito com segurança sem um domínio de sandbox isolado. Há duas etapas principais:

  • Sempre defina o cabeçalho Content-Type como um tipo MIME conhecido que seja aceito por todos os navegadores e que não contenha conteúdo ativo (em caso de dúvida, application/octet-stream é uma escolha segura).
  • Além disso, sempre defina os cabeçalhos de resposta abaixo para garantir que o navegador isole totalmente a resposta.
Cabeçalho de resposta Purpose

X-Content-Type-Options: nosniff

Impedir a detecção de conteúdo

Content-Disposition: attachment; filename="download"

Aciona um download em vez de renderização

Content-Security-Policy: sandbox

Isola o conteúdo como se ele fosse veiculado em um domínio separado

Content-Security-Policy: default-src ‘none'

Desativa a execução do JavaScript (e a inclusão de subrecursos)

Cross-Origin-Resource-Policy: same-site

Impedir que a página seja incluída em vários sites

Essa combinação de cabeçalhos garante que a resposta só possa ser carregada como uma sub-recurso pelo seu aplicativo ou baixada como um arquivo pelo usuário. Além disso, os cabeçalhos oferecem várias camadas de proteção contra bugs do navegador pelo cabeçalho de sandbox do CSP e pela restrição default-src. Em geral, a configuração descrita acima oferece um alto grau de confiança de que as respostas enviadas dessa forma não podem levar a vulnerabilidades de injeção ou isolamento.

Defesa em profundidade

Embora a solução acima represente uma defesa geralmente suficiente contra XSS, há várias outras medidas de proteção que podem ser aplicadas para fornecer mais camadas de segurança:

  • Definir um cabeçalho X-Content-Security-Policy: sandbox para compatibilidade com o IE11.
  • Defina um cabeçalho Content-Security-Policy: frame-ancestors 'none' para impedir a incorporação do endpoint.
  • Conteúdo do usuário do sandbox em um subdomínio isolado:
    • Veicular conteúdo do usuário em um subdomínio isolado (por exemplo, o Google usa domínios como product.usercontent.google.com).
    • Defina Cross-Origin-Opener-Policy: same-origin e Cross-Origin-Embedder-Policy: require-corp para ativar o isolamento de origem cruzada.

Abordagem 2: disponibilizar conteúdo para usuários ativos

O envio seguro de conteúdo ativo (por exemplo, imagens HTML ou SVG) também pode ser feito sem os pontos fracos da abordagem clássica de domínio de sandbox.
A opção mais simples é aproveitar o cabeçalho Content-Security-Policy: sandbox para informar ao navegador que ele deve isolar a resposta. Embora nem todos os navegadores da Web implementem o isolamento de processo para documentos de sandbox, os refinamentos contínuos nos modelos de processo do navegador provavelmente vão melhorar a separação do conteúdo do sandbox dos aplicativos de incorporação. Se os ataques SpectreJS e compromise de renderizador estiverem fora do seu modelo de ameaça, o uso do sandbox do CSP provavelmente será uma solução suficiente.
No Google, desenvolvemos uma solução que pode isolar totalmente o conteúdo ativo não confiável, modernizando o conceito de domínios de sandbox. A ideia principal é:

  • Crie um novo domínio de sandbox que será adicionado à lista de sufixos públicos. Por exemplo, ao adicionar exampleusercontent.com ao PSL, você pode garantir que foo.exampleusercontent.com e bar.exampleusercontent.com sejam cross-site e, portanto, totalmente isolados um do outro.
  • Os URLs que correspondem a *.exampleusercontent.com/shim são todos roteados para um arquivo de shim estático. Esse arquivo shim contém um snippet curto de HTML e JavaScript que detecta o manipulador de eventos message e renderiza qualquer conteúdo recebido.
  • Para usar isso, o produto cria um iframe ou um pop-up para $RANDOM_VALUE.exampleusercontent.com/shim e usa postMessage para enviar o conteúdo não confiável ao shim para renderização.
  • O conteúdo renderizado é transformado em um Blob e renderizado em um iframe em sandbox.

Em comparação com a abordagem clássica de domínio de sandbox, isso garante que todo o conteúdo seja totalmente isolado em um site único. E, como o aplicativo principal lida com a recuperação dos dados a serem renderizados, não é mais necessário usar URLs de recursos.

Conclusão

Juntas, essas duas soluções permitem migrar de domínios de sandbox clássicos, como googleusercontent.com, para soluções mais seguras que são compatíveis com o bloqueio de cookies de terceiros. No Google, já migramos muitos produtos para usar essas soluções e temos mais migrações planejadas para o próximo ano.