Construir uma experiência rica na web atual de forma quase inevitável envolve e conteúdo de incorporação sobre os quais você não tem controle real. Os widgets de terceiros podem impulsionar o engajamento e desempenhar um papel crucial experiência do usuário e conteúdo gerado por ele às vezes é ainda mais importante que o conteúdo nativo de um site. Abster-se de qualquer um deles não é uma opção, mas aumentam o risco de Algo BadTM acontecer no seu site. Cada que você incorpora (cada anúncio e widget de mídia social) é um potencial vetor de ataque para pessoas com intenção maliciosa:
Política de Segurança de Conteúdo (CSP) pode mitigar os riscos associados a ambos os tipos de conteúdo, fornecendo a capacidade de colocar na lista de permissões fontes de script especificamente confiáveis e outros conteúdo. Esse é um grande passo na direção certa, mas é importante notar que a proteção que a maioria das diretivas CSP oferece é binária: o recurso é é permitido ou não. Há momentos em que seria útil dizer: "Não estou certeza de que realmente confio nessa fonte de conteúdo, mas é tããããão linda! Incorporar por favor, Navegador, mas não deixe que ele prejudique meu site."
Privilégio mínimo
Em essência, estamos procurando um mecanismo que permita conceder conteúdo que incorporar apenas o nível mínimo de capacidade necessário para fazer seu trabalho. Se um widget não precisa abrir uma nova janela, e o acesso a window.open não é permitido. prejudicados. Se o Flash não for necessário, desativar o suporte ao plug-in não deve ser uma problema. Estamos o mais seguros possível se seguirmos o princípio do mínimo privilégio e bloquear todos os recursos que não sejam diretamente relevantes para a funcionalidade que gostaríamos usar. O resultado é que não precisamos mais confiar cegamente que alguma parte de conteúdo incorporado não aproveitarão os privilégios que não deveriam usar. Ela não terão acesso à funcionalidade em primeiro lugar.
Os elementos iframe
são a primeira etapa em direção a um bom framework para essa solução.
Carregar algum componente não confiável em um iframe
fornece uma medida de separação.
entre o aplicativo e o conteúdo que você quer carregar. O conteúdo emoldurado
não terá acesso ao DOM da página, aos dados armazenados localmente nem
ser capaz de desenhar em posições arbitrárias na página; o escopo dele é limitado à
contorno do frame. No entanto, a separação não é realmente robusta. A página contida
ainda tem várias opções para comportamentos irritantes ou maliciosos: a reprodução automática
vídeo, plug-ins e pop-ups são a ponta do iceberg.
O atributo sandbox
do elemento iframe
nos dá exatamente o que precisamos para restringir as restrições ao conteúdo em frames. Podemos
instruir o navegador a carregar o conteúdo de um frame específico em um privilégio baixo
permitindo apenas o subconjunto de capacidades necessárias para fazer o que for
as demandas do seu trabalho.
Vire, mas verifique
"Tweet" do Twitter é um ótimo exemplo de funcionalidade que pode ser mais incorporado com segurança no seu site por meio de uma sandbox. O Twitter permite que você incorpore a usando um iframe pelo seguinte código:
<iframe src="https://platform.twitter.com/widgets/tweet_button.html"
style="border: 0; width:130px; height:20px;"></iframe>
Para descobrir o que podemos bloquear, vamos examinar cuidadosamente quais recursos exigido pelo botão. O HTML que é carregado no frame executa um pouco JavaScript dos servidores do Twitter e gera um pop-up preenchido com uma para enviar tweets quando clicado. Essa interface precisa de acesso à conta do Twitter cookies para vincular o tweet à conta correta e precisa da capacidade para enviar o formulário de tweet. É basicamente isso. o frame não precisa carregar plug-ins, ele não precisa navegar pela janela de nível superior, ou qualquer e o número de outros bits de funcionalidade. Como eles não são necessários, vamos removê-los colocando o conteúdo do frame no sandbox.
O sandbox funciona com base em uma lista de permissões. Começamos removendo
permissões e reativar recursos individuais, adicionando
sinalizações específicas para a configuração da sandbox. Para o widget do Twitter,
decidiu ativar o JavaScript, pop-ups, envio de formulários e o site twitter.com
cookies. Podemos fazer isso adicionando um atributo sandbox
ao iframe
com o
seguinte valor:
<iframe sandbox="allow-same-origin allow-scripts allow-popups allow-forms"
src="https://platform.twitter.com/widgets/tweet_button.html"
style="border: 0; width:130px; height:20px;"></iframe>
É isso. Fornecemos à estrutura todos os recursos necessários e
navegador negará acesso a qualquer um dos privilégios
concedê-la explicitamente usando o valor do atributo sandbox
.
Controle granular sobre as capacidades
Vimos algumas das possíveis sinalizações de sandbox no exemplo acima. Agora, analisar o funcionamento interno do atributo com um pouco mais de detalhes.
Dado um iframe com um atributo sandbox vazio, o documento enquadrado será totalmente colocado em sandbox, as seguintes restrições:
- O JavaScript não será executado no documento com frame. Isso não inclui apenas JavaScript carregado explicitamente por meio de tags de script, mas também de manipuladores de eventos in-line e javascript: URLs. Isso também significa que o conteúdo em tags noscript será exibido, exatamente como se o usuário tivesse desativado o script.
- O documento emoldurado é carregado em uma origem única, o que significa que todos verificações de mesma origem vão falhar. origens exclusivas não correspondem a outras origens, até eles mesmos. Entre outros impactos, isso significa que o documento não tem acesso aos dados armazenados nos cookies de qualquer origem ou em quaisquer outros mecanismos de armazenamento (armazenamento DOM, banco de dados indexado etc.).
- O documento emoldurado não pode criar novas janelas ou caixas de diálogo (via
window.open
outarget="_blank"
, por exemplo). - Não é possível enviar os formulários.
- Os plug-ins não serão carregados.
- O documento com frame só pode navegar nele mesmo, não no arquivo pai de nível superior.
Definir
window.top.location
vai gerar uma exceção, e clicar no link comtarget="_top"
não terão efeito. - Recursos com acionamento automático (elementos de formulário com foco automático, reprodução automática vídeos etc.) estão bloqueados.
- Não foi possível acessar o bloqueio do ponteiro.
- O atributo
seamless
é ignorado naiframes
que o documento com frame contém.
Isso é muito draconiano, e um documento carregado em um iframe
totalmente no sandbox
oferece muito pouco risco. Claro, ele também não pode ter muito valor: você
usar um sandbox completo para alguns conteúdos estáticos, mas a maioria
das vezes em que você vai querer relaxar um pouco as coisas.
Com exceção dos plug-ins, cada uma dessas restrições pode ser removida adicionar uma sinalização ao valor do atributo do sandbox. Os documentos no modo sandbox nunca podem executar plug-ins, pois eles são códigos nativos fora do sandbox, mas todo o resto é justo jogo:
allow-forms
permite o envio de formulário.allow-popups
permite pop-ups (chocar!).allow-pointer-lock
permite (surpresa!) o bloqueio de ponteiro.allow-same-origin
permite que o documento mantenha a origem; páginas carregadas dehttps://example.com/
vai manter o acesso aos dados dessa origem.- O
allow-scripts
permite a execução de JavaScript e também permite que recursos para serão acionados automaticamente (porque seria simples implementar via JavaScript). allow-top-navigation
permite que o documento seja removido do frame ao ao navegar pela janela de nível superior.
Com isso em mente, podemos avaliar exatamente por que acabamos com as conjunto de sinalizações de sandbox no exemplo do Twitter acima:
allow-scripts
é obrigatório, porque a página carregada no frame executa alguns JavaScript para lidar com a interação do usuário.allow-popups
é obrigatório, porque o botão abre um formulário de tweet em uma nova janela.- O
allow-forms
é obrigatório porque o formulário de envio de tweets precisa ser de envio. allow-same-origin
é necessário, assim como os cookies de twitter.com seriam estar inacessível e o usuário não conseguiu fazer login para postar o formulário.
Uma coisa importante a observar é que as sinalizações de sandbox aplicadas a um frame também
se aplicam a quaisquer janelas ou frames criados na sandbox. Isso significa que temos
para adicionar allow-forms
ao sandbox do frame, mesmo que o formulário exista apenas
na janela que o frame aparece.
Com o atributo sandbox
, o widget recebe apenas as permissões
exige, e recursos como plug-ins, navegação superior e bloqueio de ponteiro permanecem
bloqueado. Reduzimos o risco de incorporar o widget, sem efeitos indesejados.
É uma vitória para todos os envolvidos.
Separação de privilégios
Colocar conteúdo de terceiros no sandbox para executar códigos não confiáveis em um de baixo privilégio é bastante benéfico. Mas e seu próprio código? Você confia em si mesmo, certo? Então, por que se preocupar com o sandbox?
Eu perguntaria: se seu código não precisa de plug-ins, por que dar o acesso a plug-ins? Na melhor das hipóteses, é um privilégio que você nunca usa; na pior, é uma vetor potencial para que invasores se adiantem. O código de todos tem bugs e praticamente todos os aplicativos são vulneráveis à exploração de uma forma entre outros. Colocar seu próprio código no sandbox significa que mesmo que um invasor consiga subverter seu aplicativo, ele não receberá acesso total ao origem do aplicativo; eles só poderão fazer coisas que o aplicativo poderia fazer. Ainda ruim, mas não tão ruim quanto poderia ser.
Você pode reduzir ainda mais o risco dividindo seu aplicativo em partes lógicas e colocando todas as partes no sandbox com o privilégio mínimo possível. Essa técnica é muito comum em código nativo: o Chrome, por exemplo, interrompe a si mesmo em um processo de navegador de alto privilégio que tenha acesso ao disco rígido local e pode fazer conexões de rede, e muitos processos de renderizador de baixo privilégio que fazem o trabalho pesado de analisar o conteúdo não confiável. Os renderizadores não precisam tocar disco, o navegador fornece todas as informações necessárias para para renderizar uma página. Mesmo que um hacker inteligente encontre uma maneira de corromper um renderizador, não foi muito longe, já que o renderizador não pode oferecer muito interesse por si só: todo acesso a privilégios elevados precisa ser roteado pelo processo do navegador. Os invasores terão que encontrar vários buracos em diferentes partes do sistema para causar qualquer dano, o que reduz drasticamente o risco de pwnage bem-sucedido.
Colocando eval()
no sandbox com segurança
Com a sandbox e os
API postMessage
, a
para o sucesso desse modelo é bem simples de aplicar na Web. Pedaços de
seu aplicativo pode ficar em iframe
s no modo sandbox, e o documento pai pode
a comunicação do agente entre eles postando mensagens e detectando
de resposta. Esse tipo de estrutura garante que as explorações em qualquer parte da
causar o mínimo de dano possível. Ela também tem a vantagem de forçar você a
criar pontos de integração claros para que você saiba exatamente onde precisa estar
cuidadosos ao validar entradas e saídas. Vamos usar um exemplo de brinquedo
só para ver como isso poderia funcionar.
Evalbox é um aplicativo interessante que usa uma string e a avalia como JavaScript. Uau, não é? Apenas o que que você esperava há tantos anos. É um tipo de ataque aplicativo, é claro, pois permitir a execução de JavaScript arbitrário significa que qualquer e todos os dados de uma origem estão disponíveis. Nós reduziremos o risco de Bad ThingsTM acontece garantindo que o código seja executado dentro de uma sandbox, o que o torna um pouco mais seguro. Vamos analisar o código da de dentro para fora, começando pelo conteúdo do frame:
<!-- frame.html -->
<!DOCTYPE html>
<html>
<head>
<title>Evalbox's Frame</title>
<script>
window.addEventListener('message', function (e) {
var mainWindow = e.source;
var result = '';
try {
result = eval(e.data);
} catch (e) {
result = 'eval() threw an exception.';
}
mainWindow.postMessage(result, event.origin);
});
</script>
</head>
</html>
Dentro do frame, temos um pequeno documento que simplesmente ouve as mensagens.
do pai vinculando o evento message
do objeto window
.
Sempre que o pai executa postMessage no conteúdo do iframe, esse evento
será acionado, o que nos dá acesso à string que o pai quer
a serem executados.
No gerenciador, capturamos o atributo source
do evento, que é o pai
janela. Usaremos isso para enviar o resultado de nosso trabalho árduo de volta assim que estivermos
feito. Depois, faremos o trabalho pesado, transmitindo os dados que recebemos para
eval()
: Esta chamada foi encerrada em um bloco try, porque as operações banidas
dentro de um iframe
no modo sandbox geram exceções DOM com frequência. vamos pegar
e enviar uma mensagem de erro. Por fim, publicamos o resultado
de volta à janela principal. É uma coisa bem direta.
O pai é descomplicado de forma semelhante. Vamos criar uma interface pequena com um textarea
.
para o código e um button
para execução. Além disso, extrairemos frame.html
por meio de uma
iframe
colocado no sandbox, permitindo apenas a execução de scripts:
<textarea id='code'></textarea>
<button id='safe'>eval() in a sandboxed frame.</button>
<iframe sandbox='allow-scripts'
id='sandboxed'
src='frame.html'></iframe>
Agora, vamos conectar as coisas para execução. Primeiro, ouviremos as respostas
o iframe
e alert()
para os usuários. Presumidamente um aplicativo real
faria algo menos irritante:
window.addEventListener('message',
function (e) {
// Sandboxed iframes which lack the 'allow-same-origin'
// header have "null" rather than a valid origin. This means you still
// have to be careful about accepting data via the messaging API you
// create. Check that source, and validate those inputs!
var frame = document.getElementById('sandboxed');
if (e.origin === "null" && e.source === frame.contentWindow)
alert('Result: ' + e.data);
});
Em seguida, vamos configurar um manipulador de eventos para cliques no button
. Quando o usuário
cliques, vamos pegar o conteúdo atual da textarea
e transmiti-lo à
frame para execução:
function evaluate() {
var frame = document.getElementById('sandboxed');
var code = document.getElementById('code').value;
// Note that we're sending the message to "*", rather than some specific
// origin. Sandboxed iframes which lack the 'allow-same-origin' header
// don't have an origin which you can target: you'll have to send to any
// origin, which might alow some esoteric attacks. Validate your output!
frame.contentWindow.postMessage(code, '*');
}
document.getElementById('safe').addEventListener('click', evaluate);
Fácil, não é? Criamos uma API de avaliação muito simples e podemos ter certeza de que o código avaliado não tem acesso a informações sensíveis, como cookies ou armazenamento DOM. Da mesma forma, o código avaliado não consegue carregar plug-ins, abrir novas janelas ou qualquer outra atividade irritante ou maliciosa.
Você pode fazer o mesmo para seu próprio código, dividindo aplicativos monolíticos em componentes de propósito único. Cada um pode ser encapsulado em uma simples API de mensagens, apenas como o que escrevemos acima. A janela mãe de privilégio alto pode atuar como uma controlador e agente, enviando mensagens em módulos específicos, cada um com o mínimo possível de privilégios para o trabalho, ouvindo os resultados garantindo que cada módulo seja bem abastecido apenas com as informações de que necessita.
No entanto, é preciso ter muito cuidado ao lidar com conteúdo enquadrado
que venha da mesma origem que o pai. Se uma página em
O https://example.com/
enquadra outra página na mesma origem com um sandbox
que inclua as sinalizações allow-same-origin e allow-scripts,
a página enquadrada pode alcançar o elemento pai e remover o atributo sandbox
completamente.
Jogue no sandbox
O sandbox já está disponível para você em diversos navegadores: Firefox 17+,
IE10+ e Chrome quando este artigo foi escrito (o caniuse, é claro, tem uma atualização
tabela de suporte). Como aplicar a sandbox
ao iframes
incluído, permite que você conceda determinados privilégios ao
conteúdo exibido, apenas os privilégios necessários para
para funcionar corretamente. Isso permite reduzir o risco
associadas à inclusão de conteúdo de terceiros, que vai além do que
possível com a Segurança de conteúdo
Política.
Além disso, sandbox é uma técnica poderosa para reduzir o risco de que uma equipe inteligente o invasor poderá explorar brechas em seu próprio código. Ao separar um aplicativo monolítico em um conjunto de serviços em sandbox, cada um responsável por uma um pequeno pedaço de funcionalidade independente, os atacantes serão forçados a não comprometem apenas frames específicos conteúdo, mas também seu controlador. Esse é um uma tarefa muito mais difícil, especialmente porque o controlador pode ser muito reduzido no escopo. Você poderá passar seu esforço relacionado à segurança auditando esse código se peça ajuda ao navegador com o restante.
Isso não quer dizer que a sandbox seja uma solução completa para o problema de a segurança na Internet. Ele oferece defesa em profundidade e, a menos que você controle sobre os usuários você ainda não pode contar com o suporte do navegador para todos seus usuários (se você controla os clientes, um ambiente empresarial, por exemplo: viva!). Um dia, mas, por enquanto, sandbox é outra camada de para reforçar as defesas, não é uma defesa completa na qual em que você só pode confiar. Ainda assim, as camadas são excelentes. Sugiro usar isso um.
Leitura adicional
Separação de privilégios em aplicativos HTML5 é um artigo interessante que funciona por meio do design de uma pequena estrutura, e sua aplicação a três aplicativos HTML5 existentes.
A sandbox pode ser ainda mais flexível quando combinada com dois outros novos iframe atributos:
srcdoc
, eseamless
. O primeiro permite que você preencha um frame com conteúdo sem a sobrecarga de uma solicitação HTTP, e esta permite que o estilo flua para o conteúdo emoldurado. No momento, os dois têm um suporte bastante insatisfatório para navegadores (Chrome e WebKit todas as noites). mas será uma combinação interessante no futuro. Você poderia, Por exemplo, comentários no sandbox em um artigo por meio do seguinte código:<iframe sandbox seamless srcdoc="<p>This is a user's comment! It can't execute script! Hooray for safety!</p>"></iframe>