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
eCross-Origin-Embedder-Policy: require-corp
para ativar o isolamento de origem cruzada.
- Exibição de conteúdo do usuário em um subdomínio isolado (por exemplo, o Google usa domínios como
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 quefoo.exampleusercontent.com
ebar.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 eventosmessage
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 usapostMessage
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.