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 exibir imagens enviadas por usuários (por exemplo, fotos de perfil) ou complexo como 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 temos trabalhado 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 aplicativo for example.com, você poderá veicular todo o conteúdo não confiável em exampleusercontent.com. Como esses dois domínios funcionam entre sites, qualquer conteúdo malicioso em exampleusercontent.com não afeta 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 possa não parecer necessário usar esse recurso para imagens ou downloads, isso ajuda a evitar riscos de detecção de conteúdo, especialmente em navegadores legados.
Os domínios sandbox são amplamente usados em todo o setor e têm funcionado bem há muito tempo. No entanto, eles têm duas desvantagens principais:

  • Com frequência, os aplicativos 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, isso é muito difícil de fazer com segurança. Para oferecer suporte à autenticação, os sites precisam usar URLs de recurso ou definir cookies de autenticação separados para o domínio de 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 é isolado de outros conteúdos do usuário. Isso cria o risco de conteúdo malicioso do usuário atacar outros dados no domínio do sandbox (por exemplo, lendo dados da mesma origem).

Vale ressaltar 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 veiculação de conteúdo do usuário

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

Abordagem 1: veicular conteúdo de usuário inativo

Se um site só precisar exibir conteúdo de usuários inativos (ou seja, conteúdo que não seja HTML ou JavaScript, por exemplo, 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 compatível com todos os navegadores e que não contenha 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 completamente 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 da renderização

Content-Security-Policy: sandbox

Coloca o conteúdo no sandbox 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 todos os 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 oferecem várias camadas de proteção contra bugs do navegador com o cabeçalho do sandbox da CSP e a restrição default-src. Em geral, a configuração descrita acima oferece um alto grau de confiança de que as respostas exibidas 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 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 impedir que o endpoint seja incorporado.
  • Conteúdo do usuário no 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: veicular 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 os pontos fracos da abordagem clássica de domínio de sandbox.
A opção mais simples é usar o cabeçalho Content-Security-Policy: sandbox para pedir ao navegador que isole a resposta. Embora nem todos os navegadores da Web implementem atualmente o isolamento de processos para documentos de sandbox, é provável que refinamentos contínuos em modelos de processo de navegador melhorem a separação entre conteúdo em sandbox e aplicativos incorporados. Se os ataques do SpectreJS e do comprometimento do renderizador estiverem fora do seu modelo de ameaça, usar o sandbox do CSP provavelmente é uma solução suficiente.
No Google, desenvolvemos uma solução para 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 à PSL, você garante que foo.exampleusercontent.com e bar.exampleusercontent.com sejam entre sites e, portanto, totalmente isolados um do outro.
  • Os URLs correspondentes a *.exampleusercontent.com/shim são todos encaminhados para um arquivo paliativo estático. Esse arquivo paliativo 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 fique totalmente isolado em um site exclusivo. E como o aplicativo principal deve lidar com a recuperação dos dados a serem renderizados, não é mais necessário usar URLs de capability.

Conclusão

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