Como usar eval em extensões do Chrome

O sistema de extensões do Chrome aplica uma Política de Segurança de Conteúdo (CSP) padrão bastante rigorosa. As restrições da política são diretas: o script precisa ser movido de fora da linha para uma Os arquivos JavaScript e os manipuladores de eventos inline precisam ser convertidos para usar addEventListener, e eval() é desativado. Os apps do Chrome têm uma política ainda mais rígida, e estamos satisfeitos com a segurança propriedades dessas políticas.

No entanto, reconhecemos que uma variedade de bibliotecas usam eval() e construções semelhantes a eval, como: new Function() para otimizar o desempenho e facilitar a expressão. As bibliotecas de modelos são especialmente propensos a esse estilo de implementação. Alguns (como o Angular.js) são compatíveis com a CSP. prontos para uso, muitas estruturas populares ainda não foram atualizadas para um mecanismo compatível com extensões mundo sem eval. Portanto, a remoção do suporte a essa funcionalidade comprovou mais problemáticos do que o esperado para os desenvolvedores.

Este documento apresenta o sandbox como um mecanismo seguro para incluir essas bibliotecas nos seus projetos. sem comprometer a segurança. Para fins de brevidade, usaremos o termo extensões o tempo todo, mas o conceito se aplica igualmente aos aplicativos.

Por que usar sandbox?

eval é perigoso dentro de uma extensão porque o código que ela executa tem acesso a tudo na do ambiente de alta permissão da extensão. Várias APIs chrome.* avançadas estão disponíveis e podem afetar gravemente a segurança e a privacidade do usuário; a simples exfiltração de dados é nossa menor preocupação. A solução oferecida é um sandbox em que o eval pode executar o código sem acessar os os dados da extensão ou as APIs de alto valor dela. Sem dados, APIs, sem problemas.

Fazemos isso listando arquivos HTML específicos dentro do pacote de extensão como sendo colocados no sandbox. Sempre que uma página em sandbox é carregada, ela é movida para uma origem exclusiva e será negada. acesso às APIs chrome.*. Se carregarmos essa página no modo sandbox na nossa extensão usando um iframe, poderemos passá-las, deixar que aja sobre essas mensagens de alguma forma e esperar que ela nos transmita de volta um resultado. Esse mecanismo simples de mensagens nos dá tudo o que precisamos para incluir com segurança as mensagens geradas com eval no fluxo de trabalho da nossa extensão.

Criar e usar um sandbox.

Se você quiser ir direto ao código, pegue a extensão de exemplo de sandbox e confira desativada. É um exemplo funcional de uma pequena API de mensagens criada sobre os Handlebars (em inglês). de modelos e ela deve fornecer tudo o que você precisa para começar. Para aqueles que um pouco mais de explicação, vamos analisar esse exemplo juntos aqui.

Listar arquivos no manifesto

Cada arquivo que deve ser executado em uma sandbox deve ser listado no manifesto da extensão, adicionando um sandbox. Essa é uma etapa crítica e pode ser esquecida. Portanto, verifique com atenção seu arquivo em sandbox é listado no manifesto. Neste exemplo, colocamos o arquivo no sandbox de forma inteligente. chamado "sandbox.html". A entrada do manifesto é semelhante a esta:

{
  ...,
  "sandbox": {
     "pages": ["sandbox.html"]
  },
  ...
}

Carregar o arquivo no sandbox

Para fazer algo interessante com o arquivo no modo sandbox, precisamos carregá-lo em um contexto em que ela pode ser resolvida pelo código da extensão. Aqui, sandbox.html foi carregado na Página de eventos (eventpage.html) da extensão usando um iframe. eventpage.js contém um código que envia uma mensagem para o sandbox sempre que a ação do navegador é clicada, encontrando o iframe na página e executar o método postMessage na contentWindow. A mensagem é um objeto contendo duas propriedades: context e command. Vamos falar sobre ambos em breve.

chrome.browserAction.onClicked.addListener(function() {
 var iframe = document.getElementById('theFrame');
 var message = {
   command: 'render',
   context: {thing: 'world'}
 };
 iframe.contentWindow.postMessage(message, '*');
});
Para informações gerais sobre a API postMessage, confira a documentação do postMessage sobre o MDN . Ela é bem completa e vale a pena ler. Especificamente, observe que os dados só podem ser transmitidos de um lado para outro se forem serializáveis. As funções, por exemplo, não são.

Fazer algo perigoso

Quando o sandbox.html é carregado, ele carrega a biblioteca Handlebars e cria e compila um objeto inline da mesma forma que a Handlebars sugere:

<script src="handlebars-1.0.0.beta.6.js"></script>
<script id="hello-world-template" type="text/x-handlebars-template">
  <div class="entry">
    <h1>Hello, !</h1>
  </div>
</script>
<script>
  var templates = [];
  var source = document.getElementById('hello-world-template').innerHTML;
  templates['hello'] = Handlebars.compile(source);
</script>

Isso não falha. Embora o Handlebars.compile acabe usando new Function, tudo funciona exatamente como esperado, e teremos um modelo compilado em templates['hello'].

Transmita o resultado de volta

Disponibilizaremos este modelo para uso configurando um listener de mensagens que aceite comandos. na página "Evento". Vamos usar o command transmitido para determinar o que precisa ser feito. É possível imagine fazer mais do que apenas renderizar; talvez criar modelos? Talvez gerenciá-las de alguma forma o código?), e o context será transmitido diretamente ao modelo para renderização. O HTML renderizado será retornado à página do evento para que a extensão possa fazer algo útil com ela mais tarde:

<script>
  window.addEventListener('message', function(event) {
    var command = event.data.command;
    var name = event.data.name || 'hello';
    switch(command) {
      case 'render':
        event.source.postMessage({
          name: name,
          html: templates[name](event.data.context)
        }, event.origin);
        break;

      // case 'somethingElse':
      //   ...
    }
  });
</script>

Ao voltar à página do evento, vamos receber essa mensagem e fazer algo interessante com o html dados que recebemos. Neste caso, vamos usar uma Notificação na área de trabalho, mas é totalmente possível usar esse HTML com segurança como parte da interface de usuário da extensão. Inserindo-a via O innerHTML não representa um risco de segurança significativo, já que mesmo um comprometimento total do ambiente em sandbox por algum ataque inteligente não poderiam injetar script perigoso ou conteúdo de plug-in o contexto da extensão com alta permissão.

Esse mecanismo simplifica a criação de modelos, mas ele não se limita a modelos. Qualquer um código que não funciona imediatamente sob uma Política de Segurança de Conteúdo rigorosa pode ser colocado no sandbox; no na verdade, é útil colocar no sandbox componentes das suas extensões que seriam executados corretamente para restringir cada parte do programa ao menor conjunto de privilégios necessários para sejam executados corretamente. A apresentação Como escrever apps da Web seguros e extensões do Chrome (em inglês) do Google O I/O 2012 dá alguns bons exemplos dessa técnica em ação e vale 56 minutos de sua tempo de resposta.