Voltar
Engineering
Tabela de conteúdo

 

A última década testemunhou um aumento meteórico no número de aplicativos que adotaram o modelo de aplicativo de página única (SPA). Esses aplicativos são projetados de forma que o conteúdo de cada nova página do aplicativo apareça em uma única página da Web, sem precisar carregar novas páginas HTML. Os aplicativos de página única aproveitam os recursos do JavaScript para manipular elementos do Document Object Model (DOM), permitindo atualizar todo o conteúdo na mesma página.

Uma arquitetura de página da Web mais tradicional carrega páginas diferentes à medida que o usuário tenta abrir uma nova página da Web. Nesse caso, cada página HTML geralmente está vinculada a outras páginas. A cada carregamento de página, o navegador busca e exibe uma nova página.

Já os aplicativos de página única são habilitados por estruturas JavaScript, como React, Angular e VueJS. Essas estruturas ajudam a ocultar as funções complexas que os SPAs executam. Eles também oferecem benefícios adicionais, como componentes modulares reutilizáveis, gerenciamento de estado etc. Essas estruturas modernas ajudam os SPAs a executar páginas da Web sem esforço, em comparação com aplicativos de várias páginas que usam JavaScript Vanilla. Usar JavaScript simples dificulta manter a interface do usuário atualizada com mudanças dinâmicas de estado.

O surgimento de tais estruturas causa mudanças nas implicações de segurança que elas podem ter no frontend. Portanto, é necessário entender seu mecanismo interno ao desmontar o código do lado do cliente e como as estruturas modernas alteram os vetores de ataque.

 

Processo de construção

Como de costume, um desenvolvedor cria uma página da Web definindo a estrutura da página em um arquivo HTML e seu estilo em um arquivo CSS. Em seguida, ele é vinculado em HTML usando tags como <style>ou<link>, incorporando código JavaScript com <script>tags. No entanto, criar aplicativos de página única é mais complicado do que isso.

Frameworks como React e Vuejs fornecem um DOM virtual, que permite que você evite HTML bruto. Um DOM virtual, ao contrário de um DOM normal, é um conceito no qual a representação da interface do usuário é armazenada na memória, em vez de renderizá-la no navegador a cada alteração. Isso permite mudanças mais rápidas no DOM. O código também é escrito em arquivos JavaScript modulares que são processados no processo de construção.

Aqui está um diagrama com uma visão geral do processo de criação:

 

Single-page application build process

 

Transpilando

JavaScript é um dialeto do ECMAScript e não é um padrão fixo; novos recursos são adicionados ao ECMAScript com novos padrões, a cada poucos anos. Embora os padrões mais novos sejam lançados com frequência, nenhum mecanismo JavaScript do navegador (Chromium V8, Safari Javascript Core, Firefox SpiderMonkey) implementa totalmente todas as especificações ECMA, cada uma com certas diferenças nos recursos que suportam. Além disso, seu código precisa ser compatível com navegadores mais antigos lançados para suportar apenas especificações mais antigas.

A transpilação permite que você escreva código moderno que é então convertido em padrões e recursos suportados em todos os lugares, por exemplo, ES6 -> ES5. Você pode usar const, deixe para definir suas variáveis, mas o transpilador ajuda a convertê-las em var, internamente.

A transpilação também é usada para converter código em dialetos JavaScript, como Typescript e Coffeescript, em JavaScript simples. Cada dialeto contém seu próprio transpilador, como tsc para Typescript. Babel é o transpilador mais comum.

 

Empacotamento

Um aplicativo moderno típico contém centenas de dependências externas quando você o analisa recursivamente e não é prático carregar cada uma delas separadamente em uma tag de script.

Por meio do processo de agrupamento, você pode pegar cada instrução de importação ou exigência, localizar os arquivos de origem e agrupá-los em um único arquivo JavaScript, aplicando o escopo apropriado.

Com a ajuda desse processo, todo o código que faz o aplicativo funcionar é agrupado; o código da lógica de negócios, bem como o código padrão, são agrupados.

 

Minificação/Ofuscação

A saída final do JavaScript geralmente pode ser muito grande devido a espaços extras ou dados redundantes e desnecessários. Portanto, a etapa final do processo de criação é minimizar o código. Durante o processo de minificação, os comentários/espaços em branco são removidos, os identificadores são renomeados para variantes mais curtas e certas rotinas são simplificadas. Esse processo então leva à ofuscação, em que o código final é ilegível e difere muito do código-fonte.

O empacotamento e a minificação geralmente são feitos usando Webpack.

 

Single-page application: Original code - easy to read
Código original — fácil de ler

 

Single-page application: Transpiled, Bundled, Minified final output - unreadable
Saída final transpilada, agrupada e minimizada — ilegível

 

Código de engenharia reversa

Depois que o código for minimizado no processo de criação, os pesquisadores de segurança precisarão desminificá-lo para estudar o aplicativo. Como a maioria das informações, como nomes de variáveis, é perdida durante o processo de minimização, não há uma maneira simples de fazer isso. No entanto, existem certas ferramentas para ajudá-lo no processo, como jsnice.org.

jsnice

Essa ferramenta usa aprendizado de máquina para restaurar variáveis reduzidas, nomes de funções e inferir informações de tipo. Ele também formata o código e adiciona comentários.

Após essa etapa, você ainda terá um código agrupado, mas a lógica principal seria legível.

Para dividi-lo em módulos, precisamos saber como o Webpack ou qualquer outro empacotador funciona.

 

Desagrupamento

Um empacotador começa com um arquivo de ponto de entrada — a raiz da lógica do seu aplicativo — e rastreia as instruções de importação e exigência. Em seguida, ele cria um gráfico de dependência, o módulo A requer B, que requer C e D, e assim por diante.

Se você examinar os pedaços do Webpack depois de passarem por eles jancice<number>, você encontrará muitas chamadas para “__webpack_require__ ()”.

__webpack_require__” é semelhante ao exigir Sintaxe JavaScript na funcionalidade, exceto que contém a lógica para carregar módulos a partir dos blocos.

A única maneira de separar um pacote é construir a árvore de sintaxe abstrata (AST) manualmente, pois houve tentativas de debundle que não são mais mantidos.

Você pode usar esses recursos para estudar em profundidade como funciona um arquivo de pacote e conhecer o componentes internos do Webpack. Neste vídeo, o fundador do Webpack, Tobias Koppers, nos mostra como o empacotamento é feito manualmente.

 

Dica de segurança

Single-page application security

Como essas estruturas alteram os vetores de ataque?

XSS reduzido

Reaja não renderiza HTML dinâmico; ele limpa o conteúdo proveniente de todas as variáveis, mesmo que elas não contenham conteúdo dinâmico. Aqui, o XSS está praticamente erradicado, a menos que o desenvolvedor use uma função insegura, como Definido perigosamente em HTML interno.

Nesse caso, mesmo se você encontrar reflexão de dados, não conseguirá inserir HTML.

 

CSP - desvia

Você pode usar certos gadgets disponíveis em Angular para ignorar a Política de Segurança de Conteúdo (CSP) em certos casos.

<img src=”/” ng-on-error=” $event.srcElement.ownerDocument.defaultView.alert ($event.srcElement.ownerDocument.domain)”

/>

Aqui, se o CSP estiver bloqueando scripts embutidos, mas a página estiver usando Angular, você poderá usar o atributo ng-on-error para fazer com que o Angular execute o JavaScript. Esses tipos de gadgets geralmente são corrigidos, mas descobertos regularmente no Vuejs e no Angularjs.

 

Nenhum item encontrado.

Blogs relacionados