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

David Dworken
David Dworken

Muitos aplicativos da Web precisam exibir conteúdo controlado pelo usuário. Isso pode ser tão simples quanto veicular 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). Sempre foi difícil fazer isso 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 o que chamamos de domínios de sandbox. A ideia básica é que, se o domínio principal do seu aplicativo for example.com, você poderá exibir todo o conteúdo não confiável no exampleusercontent.com. Como esses dois domínios são entre sites, nenhum conteúdo malicioso no exampleusercontent.com pode afetar o example.com.
Essa abordagem pode ser usada para exibir 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 esse recurso para imagens ou downloads, isso ajuda a evitar riscos de interceptação de conteúdo, especialmente em navegadores legados.
Os domínios sandbox são amplamente usados em todo o setor e funcionam bem há muito tempo. Mas há duas grandes desvantagens:

  • 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 sandbox intencionalmente não compartilham cookies com o domínio principal do aplicativo, isso é muito difícil de fazer de forma segura. Para oferecer suporte à autenticação, os sites precisam usar URLs de recurso 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 esteja isolado do site principal, ele não fica isolado dos outros conteúdos do usuário. Isso aumenta o risco de um conteúdo malicioso do usuário atacar outros dados no domínio da sandbox (por exemplo, por meio da leitura de dados de mesma origem).

Também é importante notar que os domínios de sandbox ajudam a reduzir os riscos de phishing, uma vez que os recursos são claramente segmentados em um domínio isolado.

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

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

Abordagem 1: veiculação de conteúdo inativo do usuário

Se um site só precisar exibir conteúdo inativo do usuário (ou seja, um conteúdo que não seja HTML ou JavaScript, como imagens e downloads), isso poderá 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 com garantia de não ter conteúdo ativo (em caso de dúvida, application/octet-stream é uma opção 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

Impede a interceptação de conteúdo

Content-Disposition: attachment; filename="download"

Aciona um download em vez de uma renderização

Content-Security-Policy: sandbox

Coloca o conteúdo no sandbox como se ele tivesse sido veiculado em um domínio separado.

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

Desativa a execução do JavaScript (e a inclusão de quaisquer sub-recursos)

Cross-Origin-Resource-Policy: same-site

Impede que a página seja incluída entre sites

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

Defesa em profundidade

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

  • Defina um cabeçalho X-Content-Security-Policy: sandbox para compatibilidade com o IE11.
  • Defina um cabeçalho Content-Security-Policy: frame-ancestors 'none' para bloquear a incorporação do endpoint.
  • Use o sandbox do conteúdo do usuário em um subdomínio isolado por:
    • Exibição de 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: veiculação de conteúdo de usuários ativos

A veiculação segura de conteúdo ativo (por exemplo, imagens HTML ou SVG) também pode ser feita sem as fragilidades da abordagem clássica de domínios de sandbox.
A opção mais simples é usar o cabeçalho Content-Security-Policy: sandbox para instruir o navegador a isolar a resposta. Embora nem todos os navegadores da Web implementem atualmente o isolamento de processo para documentos de sandbox, os refinamentos contínuos dos modelos de processo do navegador provavelmente vão melhorar a separação do conteúdo em sandbox e dos aplicativos de incorporação. Se os ataques de SpectreJS e comprometimento do renderizador estiverem fora do seu modelo de ameaça, usar o sandbox da 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 ao modernizar o conceito de domínios de sandbox. A ideia central é:

  • Crie um novo domínio de sandbox que seja adicionado à lista de sufixos públicos. Por exemplo, ao adicionar exampleusercontent.com ao PSL, você garante que foo.exampleusercontent.com e bar.exampleusercontent.com estejam entre sites e, portanto, totalmente isolados um do outro.
  • Os URLs correspondentes a *.exampleusercontent.com/shim são todos roteados para um arquivo shim estático. Esse arquivo shim contém um pequeno snippet 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 paliativo para renderização.
  • O conteúdo renderizado é transformado em um Blob e renderizado dentro de 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. Além disso, 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 o googleusercontent.com, para soluções mais seguras e 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.