Enterprise Architecture


Comunicação segura na travessia entre domínios no navegador
Publicado em: 12 de novembro de 2007
Por Danny Thorpe

Resumo: Um consumidor pode entrar em praticamente qualquer loja e fazer uma compra apresentando apenas um cartão de plástico e uma identidade com foto. O comprador e o lojista sequer precisam trabalhar com a mesma moeda, falar o mesmo idioma ou ter a mesma nacionalidade. Eles realmente precisam compartilhar um sistema global de comunicações e uma rede global de bancos que permitam ao comprador carregar consigo, para qualquer lugar, os seus serviços bancários, e que forneçam apoio de infra-estrutura ao lojista. E se a Internet pudesse fornecer proteções e serviços similares para que usuários e administradores de sites da Web pudessem compartilhar informações?

Desenvolver aplicativos que residem no navegador da Web é muito parecido com olhar as vitrines da Main Street: muitas lojas onde comprar, milhares de coisas lindas para serem admiradas nas vitrines de cada loja, mas impossíveis de adquirir. Frau Browser, a sua cruel madrasta alemã, puxa a sua coleira sempre que você pensa em chegar mais perto do vidro. Ela diz que é para o seu próprio bem, mas você começa a desconfiar que a guia da coleira, assim curta, é mais para a comodidade dela do que para sua segurança.

Os navegadores Web isolam páginas que residem em vários domínios para evitar que fiquem espiando as informações, umas das outras, sobre o usuário final. Nos primeiros momentos da Internet, esse modelo de isolamento era adequado porque poucos eram os sites que adicionavam lógica de aplicativo significativa no cliente do navegador e, mesmo aqueles que o faziam estavam apenas acessando dados do próprio servidor. Cada servidor Web era o seu próprio silo, só com vínculos HTML para conteúdo externo.

Isso não é a Internet de hoje. A experiência da Internet evoluiu para agregar dados de vários domínios. Esta agregação é dirigida pela personalização de sites pelo usuário, assim como sites que adicionam valor agrupando as combinações de diversas fontes de dados. Neste mundo, o modelo de isolamento de domínio do navegador Web torna-se um enorme obstáculo, impedindo o desenvolvimento do aplicativo Web do lado do cliente. Para evitar esse obstáculo, os programadores de aplicativos Web estão, cada vez mais, transferindo lógica de aplicativo para os seus servidores Web, sacrificando a escalabilidade do servidor, apenas para fazer o que precisa ser feito. Enquanto isso, o terminal “burro” de 2GHz e 2GB do usuário está inativo.

Se os PCs fossem construídos como um navegador Web, seria possível gravar os dados no disco, mas seria impossível usar esses arquivos com qualquer outro aplicativo da máquina ou na máquina de qualquer outra pessoa. Se você decidisse trocar para outro produto editor de fotos, não poderia editar nenhuma das fotos antigas. Se você reclamasse com os fabricantes do seu editor de fotos anterior, eles não ligariam a mínima e declarariam "Não sabemos o que o outro editor de fotos poderia fazer com os seus dados. Como nós não conhecemos nem confiamos nesse outro editor de fotos, você também não deve! E, não, nós não o deixaremos usar as suas fotos com esse editor, porque somos nós que lhe fornecemos o espaço de armazenamento e, na verdade, as suas fotos são parcialmente nossas".

Você não conseguiria nem localizar as suas fotos, a não ser que soubesse, primeiramente, em qual aplicativo elas foram criadas. "Qual editor de fotos usei para fotografar o aniversário do Stevie? Não consigo localizá-las!"

E, o que acontece quando aquele editor de fotos de vanguarda, supermoderno, falir e desaparecer? Todas as suas fotos desaparecerão com ele!

Já viu esse filme antes? Acontece com todos nós, todos os dias, usando sites e aplicativos Web. O isolamento de domínio evita que você use as suas listas de música para comprar canções similares em uma loja online independente (desvinculada do fabricante do MP3 player) ou em um quiosque de uma loja de varejo.

O isolamento de domínio também dificulta muito a construção de aplicativos Web leves de infra-estrutura baixa que “retalham e dividem” dados extraídos dos vários servidores de dados de uma rede corporativa. Um subdomínio "foo.bar.com" de sua rede corporativa interna "bar.com" é tão isolado da "bar.com" e "bee.bar.com" como de seus endereços externos, por exemplo, xyz.com.

No entanto, ninguém pensa em abrir completamente suas defesas e sair distribuindo flores... As ameaças aos dados e à segurança pessoal, que a rígida política de isolamento de domínio do navegador protege, é real e maldosa. Com reflexão e infra-estrutura cuidadosas, pode haver um meio bom que proporcione maior benefício ao usuário mantendo, ao mesmo tempo, as práticas de segurança necessárias. Os usuários devem ter controle sobre quando, o quê e quanto de suas informações estão disponíveis para um determinado site da Web. Não se trata de liberar o fluxo das informações em todas as direções, mas de dar liberdade aos usuários para usar os próprios dados no local e no momento em que isso atender aos seus objetivos, independentemente do local de residência dos dados.

Precisa-se de um meio para que o navegador suporte o acesso legítimo aos dados da travessia entre domínios, sem comprometer a segurança e o controle de dados do usuário final.

Um grande passo nessa direção é a proposta de desenvolvimento de padrõesa, organizada por Ian Hickson, com o objetivo de estender o xmlHttpRequest para dar suporte às conexões da travessia entre domínios utilizando opt-in/opt-out baseado em domínio pelo servidor solicitado no momento. (Vide referências.) Se esse procedimento sobreviver à revisão dos colegas e for implementado por grande parte dos navegadores, haverá esperança de se diminuir a barreira da travessia entre domínios para usos legítimos e, ainda, oferecer proteção contra usos ilegítimos. Realisticamente, todavia, muito tempo passará antes da implementação desta proposta pela maioria dos navegadores de todo o setor.

Figura 1: transferência de dados do iframe URL

Bb735305.Browser_01(pt-br,MSDN.10).jpg

Agora, o que se pode fazer? Existem padrões de comportamento compatíveis com todos os navegadores que aceitam código JavaScript residente em um contexto de domínio de navegador, para observar as alterações feitas pelo JavaScript residente em outro contexto de domínio, no âmbito da mesma instância de navegador. Por exemplo, as mudanças feitas na propriedade largura ou altura de um iframe são observáveis nas partes interna e externa do iframe. Outro exemplo é a propriedade iframe.src. O código externo ao iframe não pode ler a propriedade src URL do iframe, mas pode escrever nela. Dessa forma, o código externo ao iframe pode enviar dados para dentro do iframe por intermédio do URL do iframe.

Esta técnica de URL tem sido usada por programadores Web desde a primeira introdução dos iframes no HTML, mas os usos são tipicamente primitivos, construídos para um fim específico e agrupados sem critério. E o que é pior, transferir dados por iframe src URL pode criar um vetor exploit, permitindo que códigos maliciosos corrompam o estado do seu aplicativo Web, jogando lixo no seu iframe. Qualquer código, em qualquer contexto do navegador, pode escrever na propriedade src do iframe e o iframe receptor não saberá qual a origem dos dados do URL. Na grande parte das situações, nunca é possível confiar em dados de origem desconhecida.

Este artigo explorará as questões e as soluções técnicas do canal seguro de dados da travessia entre domínios do lado do cliente, desenvolvido pelo grupo da Windows Live Developer Platform.

Nesta página

Técnica IFrame URL
Ocultar dados em marcadores
Identificação do remetente
Envio para o remetente
Destinatário stateful
Aplicação de idéias
Capacitação do usuário
Agradecimentos
Referências
Sobre o autor

Técnica IFrame URL

Um iframe é um elemento HTML que encapsula e exibe um documento HTML, completo, dentro de si mesmo, permitindo ao usuário exibir um documento HTML dentro de outro. Vamos chamar a página externa ou a página host de pai do iframe e a página interna, de conteúdo do iframe. A página interna do iframe é especificada atribuindo-se uma URL à propriedade src do iframe.

Quando o URL fonte do iframe tem o mesmo nome de domínio do externo, página host, o JavaScript da página host pode navegar pelo DOM interno do iframe e visualizar todo o seu conteúdo. Inversamente, o iframe pode navegar até sua cadeia pai e visualizar todos os seus irmãos DOM na página host e as respectivas propriedades. Entretanto, quando o URL fonte do iframe tem um domínio diferente daquele da página host, o host não pode visualizar o conteúdo do iframe e este não pode visualizar o conteúdo da página host.

Mesmo que o host não possa ler a propriedade src do elemento iframe, ele ainda poderá escrever nele. A página host não conhece o conteúdo que o iframe exibe, no momento, mas pode fazer com que o iframe exiba outra coisa.

Cada vez que um novo URL é atribuído à propriedade src do iframe, este passará por todas as etapas normais de carregamento de uma página, inclusive disparar o evento onLoad.

Temos agora todas as peças necessárias para transferir dados do host para o iframe no URL. (Vide Figura 1.) A página host do domínio foo.com pode colocar um pacote de dados codificado em URL ao final de um URL de documento existente no domínio bar.com. Os dados podem ser carregados no URL como um parâmetro de consulta usando o caractere ? (http://bar.com/receiver.html?datadatadata) ou como um marcador, usando o caractere # (http://bar.com/receiver.html#datadatadata). Existe uma enorme diferença entre esses dois tipos de URL, que discutiremos logo a seguir.

“O ISOLAMENTO DE DOMÍNIO DIFICULTA MUITO A CONSTRUÇÃO DE APLICATIVOS WEB LEVES E DE INFRA-ESTRUTURA BAIXA, QUE RETALHAM E DIVIDEM DADOS EXTRAÍDOS DOS VÁRIOS SERVIDORES DE DADOS DE UMA REDE CORPORATIVA. NO ENTANTO, NINGUÉM PENSA EM ABRIR COMPLETAMENTE SUAS DEFESAS E SAIR DISTRIBUINDO FLORES".

A página host atribui este URL à propriedade src do iframe. O iframe carrega a página e dispara a rotina de tratamento do evento onLoad da página. A rotina de tratamento do evento onLoad da página do iframe pode procurar seu próprio URL, localizar o pacote de dados integrado e decodificá-lo para decidir o que deve ser feito a seguir.

Isto resume a técnica de transferência de dados do URL iframe à sua forma mais simples. O host constrói um string URL de um URL de documento conhecido + carga de dados, faz a sua atribuição à propriedade src do iframe, o iframe "acorda" na rotina de tratamento do evento onLoad e recebe a carga de dados. O que mais você poderia querer?

Na verdade, muito mais. Esta técnica simples traz em si muitas limitações:

  • nenhuma confirmação de recebimento. A página host não sabe se o iframe recebeu os dados, com sucesso;

  • substituição de mensagens. O host não sabe quando o iframe conclui o processamento da mensagem anterior e, assim, não sabe quando é seguro enviar a próxima mensagem;

  • limites de capacidade. O URL não pode ser infinitamente longo e o limite de comprimento varia de acordo com a família do navegador. O Firefox é compatível com URLs de até 40k, mais ou menos, mas o IE define o limite em menos de 4k. Qualquer ocorrência mais longa será truncada ou ignorada;

  • dados de origem desconhecida. O iframe não sabe quem coloca os dados no seu URL. Os dados podem vir de uma página host amigável, como no nosso caso, foo.com ou podem ser códigos maliciosos da evil.com na bar.com, esperando que qualquer coisa 'pegue ou estoure;

  • sem respostas. Não é possível ter um script no iframe que devolva dados para a página host;

  • perda de contexto. Como a página é recarregada a cada mensagem, a página interna do iframe não pode atualizar o estado global em todas as mensagens.

Ocultar dados em marcadores

Devemos usar ? ou # para vincular dados ao final do iframe URL? Embora bastante inócuas na superfície, existem, de fato, algumas diferenças significativas sobre como os navegadores tratam os URLs se forem utilizados parâmetros de consulta ou URLs com marcadores. Dois URLs com o mesmo caminho de base, mas parâmetros de consulta diferentes são tratados como URLs diferentes. Aparecerão separadamente na lista de histórico do navegador, serão entradas separadas no cache da página do navegador e gerarão solicitações de trabalho separadas em toda a rede.

Os marcadores URL foram projetados para fazer referência a tags-âncora especialmente marcados em uma página. O navegador considera dois URLs com o mesmo caminho de base, mas com texto de marcador diferente após o caractere #, como o mesmo URL no que se refere ao histórico e aos caches do navegador. Os marcadores diferentes estão apenas apontando para partes diferentes da mesma página (URL), mas, apesar de tudo, é a mesma página.

“DEVEMOS USAR ? OU # PARA VINCULAR DADOS AO FINAL DO IFRAME URL? EMBORA BASTANTE INÓCUAS NA SUPERFÍCIE, EXISTEM, DE FATO, ALGUMAS DIFERENÇAS SIGNIFICATIVAS SOBRE COMO OS NAVEGADORES TRATAM OS URLS SE FOREM UTILIZADOS PARÂMETROS DE CONSULTA OU URLS COM MARCADORES.

Os URLs http://bar.com/page.html#one, http://bar.com/page, html#two e http://bar.com/page.html#three são considerados pelo navegador equivalentes ao cache de http://bar.com/page.html. Se parâmetros de consulta tiverem sido utilizados, o navegador verá três URLs diferentes e três viagens pela rede. Contudo, se usarmos marcadores teremos, no máximo, uma viagem pela rede; as solicitações subseqüentes serão preenchidas a partir do cache local do navegador. (Vide Figura 2.)

Para os casos em que precisamos enviar grande volume de mensagens pelo iframe URL usando o mesmo URL de base, os marcadores são perfeitos. As cargas de dados na porção do marcador do URL não aparecerão no histórico do navegador, nem no cache da sua página. E, ainda mais: as cargas de dados nunca atravessarão a rede depois que a carga da página inicial estiver no cache!

Os dados transferidos entre a página host e o iframe não podem ser visualizados por quaisquer outros elementos DOM da página host porque o iframe está em um contexto de domínio diferente daquele da página host. Os dados não aparecem no cache do navegador e não atravessam a rede; assim, faz sentido dizer que os pacotes de dados são apenas observáveis pelo iframe receptor ou por outras páginas servidas pelo domínio bar.com.

Figura 2: Equivalência cache de URLs de marcador

Bb735305.Browser_02(pt-br,MSDN.10).jpg

Figura 3: mensagem em uma garrafa de Klein

Bb735305.Browser_03(pt-br,MSDN.10).jpg

Identificação do remetente

Talvez o maior problema de segurança com a simples técnica de transferência de dados para o iframe URL seja a dúvida sobre a origem dos dados. Incorporar o nome do remetente ou alguma forma de ID do aplicativo não resolve, pois isso pode ser facilmente copiado por fraudadores. É preciso ter uma forma para que a mensagem, implicitamente, identifique o remetente de modo a não ser facilmente copiada.

Para muitas pessoas, a primeira solução que vem à mente seria usar um tipo de criptografia, com chaves cuja posse ficasse apenas com o remetente e o destinatário. Este procedimento seria eficiente, mas é uma solução extremamente manual, especialmente quando existe o envolvimento do JavaScript.

Existe uma outra forma, que aproveita a importância crítica da identidade do nome do domínio no ambiente do navegador. Se eu posso enviar uma mensagem secreta para você usando o seu nome de domínio e se, mais tarde, eu receber esse segredo como parte de um pacote de dados, poderei deduzir, razoavelmente, que o pacote de dados tem origem no seu domínio.

A única forma do segredo vir de um domínio diferente será se o seu domínio ou o navegador do usuário estiverem comprometidos ou se o meu DNS estiver comprometido. Todas as apostas estarão erradas se o seu domínio ou navegador estiver comprometido. Se a contaminação do DNS é uma preocupação real, pode-se usar https para constatar se as solicitações de resposta do servidor para um determinado nome de domínio são, de fato, do servidor legítimo.

Se o remetente fornecer o segredo ao destinatário, o destinatário fornecer o segredo ao remetente e os dois segredos forem transportados em todos os pacotes de dados transmitidos pelo canal de dados do iframe URL, as duas partes poderão ter confiança quanto à origem de cada mensagem. Códigos maliciosos incluídos por evil.com podem ser facilmente reconhecidos e descartados. A troca de segredos é inspirada pelo protocolo de três fases SSL/https.

Esses segredos não precisam ser complexos, nem criptografados, já que os pacotes de dados enviados pelo canal de dados do iframe URL não são visíveis a terceiros. Números randômicos funcionam bem quando usados como segredos, com um porém: o gerador de número randômico do JavaScript (Math. random()) não é robusto em termos de criptografia e, portanto, é um risco na produção de seqüências de números previsíveis. O Firefox fornece um gerador de número randômico robusto para esse fim (crypto.random()), mas o IE não. Assim sendo, em nossa implementação optamos por gerar números randômicos robustos no servidor Web, enviando-os para o cliente, conforme necessário.

Envio para o remetente

A maioria dos problemas associados à técnica de transferência de dados do iframe URL fica reduzida na geração de respostas. Confirmar o recebimento de pacotes exige que o destinatário envie uma resposta ao remetente. Trocar segredos exige respostas bidirecionais. Controlar o fluxo das mensagens e quebrar grandes volumes de dados em várias mensagens menores exige confirmação de recebimento.

E então, como o iframe pode voltar a se comunicar com a página host? Não indo para cima, mas para baixo. O iframe não pode atribuir a nada em seu pai porque os dois residem em contextos de domínio diferentes. Mas o iframe bar.com (A) pode conter outro iframe (B) e ‘A’ pode atribuir um URL à propriedade src de ‘B’ no domínio da página host (foo.com). A página host foo.com contém o iframe bar.com (A) que contém o iframe foo.com (B).

Ótimo, mas o que aquele iframe interno pode fazer? Não muito com o seu pai, o iframe bar.com. Mas, suba um nível e você encontrará ouro: o pai do pai de 'B' é a página host em foo.com. A página de 'B' está em foo.com, B.parent.parent está em foo.com e, assim, 'B' pode ter acesso a tudo na página host e chamar as funções do JavaScript no contexto da página host.

A página host pode transferir dados para o iframe ‘A’ escrevendo um URL em uma propriedade src de ‘A’. 'A' pode processar os dados e enviar uma confirmação para o host escrevendo um URL na propriedade src de 'B'. 'B' acorda em seu evento onLoad e transfere a mensagem até o seu pai do pai, a página host. Pronto! Confirmação de ida e volta de uma série de pipes unidirecionais conectados juntos de tal forma que, provavelmente, seria muito divertido para Felix Klein, o matemático e faz-tudo.

Destinatário stateful

Para manter o estado global no contexto de bar.com em todas as várias mensagens enviadas para o iframe, use dois iframes com as páginas de bar.com. Use um dos iframes como um destinatário de mensagem stateless, recarregando e perdendo o seu estado com todas as mensagens recebidas. Coloque a lógica do aplicativo stateful do lado bar.com em outro iframe. Reduza a lógica da página iframe do mensageiro ao essencial exigido para transferir os dados recebidos ao iframe stateful bar.com.

“SE EU POSSO ENVIAR UMA MENSAGEM SECRETA PARA VOCÊ USANDO O SEU NOME DE DOMÍNIO E SE, MAIS TARDE, EU RECEBER ESSE SEGREDO COMO PARTE DE UM PACOTE DE DADOS, PODEREI DEDUZIR, RAZOAVELMENTE, QUE O PACOTE DE DADOS TEM ORIGEM NO SEU DOMÍNIO. SE A CONTAMINAÇÃO DO DNS É UMA PREOCUPAÇÃO REAL, PODE-SE USAR HTTPS PARA CONSTATAR SE AS SOLICITAÇÕES DE RESPOSTA DO SERVIDOR PARA UM DETERMINADO NOME DE DOMÍNIO SÃO, DE FATO, DO SERVIDOR LEGÍTIMO."

Um iframe não pode enumerar os filhos de seu pai para localizar outros irmãos de bar.com, mas pode procurar um iframe irmão usando window.parent.frames[], se ele souber o nome do iframe irmão. Cada vez que é recarregado para receber novos dados no URL, o iframe mensageiro pode procurar o seu iframe irmão stateful bar.com utilizando window.parent.frames[] e chamar uma função do iframe stateful para transferir os dados da nova mensagem para o iframe stateful. Assim, o contexto do domínio bar.com na memória do navegador pode acumular pedaços de mensagem, de várias mensagens, para reconstruir uma carga de dados superior ao comprimento máximo do URL do navegador.

Aplicação de idéias

A equipe da Windows Live Developer Platform desenvolveu essas idéias em uma biblioteca de "canal" do JavaScript. Esses canais de travessia entre domínios são usados na implementação dos controles Web de Windows Live Contacts e Windows Live Spaces (http://dev.live. com), destinados a residir em páginas Web de terceiros, mas executados em um iframe seguro no contexto do domínio live.com. Os controles fornecem aos sites de terceiros acesso controlado de usuários aos dados do Windows Live como, por exemplo, a lista de contatos do usuário ou álbuns de fotos do Space. O objeto (canal) é compatível com o envio arbitrário de grandes volumes de dados pelas fronteiras de domínio do iframe com confirmação de recebimento, controle de fluxo de mensagens, partição de mensagens, identificação do remetente e tudo isso acontece de modo oculto.

Nossa meta é cuidar para que este código de canal seja uma biblioteca reutilizável, disponível a todos os parceiros internos da Microsoft e também de desenvolvedores Web de terceiros. O código está sendo bem executado em seus contextos atuais mas, ainda assim, temos de executar alguns trabalhos nas áreas de auto-diagnóstico e localização de defeitos - se as pontas deste canal estiverem configuradas corretamente, ele trabalha muito bem, mas pode ser um grande pesadelo descobrir o que não está muito certo, no momento em que você for configurá-lo pela primeira vez. O principal obstáculo é o próprio navegador: tentar ver o que (não) está acontecendo em diferentes contextos do domínio é um grande desafio, quando o navegador não mostra o que acontece do outro lado do muro.

Capacitação do usuário

Há pouco menos de 40 anos, um consumidor da Main Street, EUA, tinha de se esforçar bastante para convencer um lojista a aceitar o pagamento. Se não tivesse dinheiro vivo (muito!), provavelmente você estaria sem sorte. Se tivesse outra moeda, precisaria encontrar um grande banco, em uma grande cidade, para fazer o câmbio para a moeda local. Cheques de outra praça eram raramente aceitos e crédito direto da loja era oferecido apenas aos residentes locais.

Hoje, consumidores e lojistas compartilham sistemas de comunicações e redes de bancos globais que permitem ao comprador levar consigo os serviços bancários aonde quer que vá, e ajuda o lojista a realizar vendas as quais, de outra forma, poderia perder. A rede bancária também fornece suporte de infra-estrutura ao lojista, ajudando-o com câmbio, dando cobertura contra o risco de crédito e reduzindo os prejuízos decorrentes de fraude.

Agora, por que a Internet não pode fornecer proteções e capacitações similares ao usuário itinerante da Web, assim como serviços de infra-estrutura para administradores de sites na Web? Levar os dados e a experiência consigo enquanto passa de um site para outro (da mesma forma que um cartão de débito oferece os seus serviços de banco a você durante as compras), revelando informações aos administradores de sites apenas a seu critério. A Internet vai chegar lá - é apenas uma questão de tempo e qualidade.

Agradecimentos

Obrigado a Scott Issacs pelo conceito original da transferência de dados do iframe URL. Meus agradecimentos a Yaron Goland e Bill Zissimopoulos por suas importantes contribuições às primeiras implementações e à depuração do código de canal e a Gabriel Corverra e Koji Kato pelo trabalho nas iterações mais recentes. “É absolutamente insano, mas até pode ser que funcione!”

Referências

XMLHttpRequest 2, Ian Hickson http://www.mail-archive.com/public-webapi@w3.org/msg00341.html

http://lists.w3.org/Archives/Public/public-webapi/2006Jun/0012.html

Weblog de Anne van Kesteren: http://annevankesteren.nl/2007/02/xxx

Sobre o autor

Danny Thorpe é desenvolvedor da equipe da Windows Live Developer Platform. Está escrito no seu crachá: Principal SDE, mas ele prefere “Windows Live Quantum Mechanic” já que gasta muito mais tempo persuadindo com paciência pequenos bits teimosos a migrar por barreiras impenetráveis. Em outras vidas, ele trabalhou com "tecnologia de navegador não divulgada" na Google e, antes disso, era cientista-chefe na Borland e arquiteto-chefe do compilador Delphi. Na Borland, teve a boa sorte de trabalhar com Anders Hejlsberg, Chuck Jazdzewski, Eli Boling e muitas outras lendas da Borland como mentores. Não se lembra dos acontecimentos anteriores ao ingresso na Borland, porque era muito jovem. Visite o seu blog no endereço: http://blogs.msdn.com/dthorpe.

Page view tracker