Encodera

Спасибо, что читаете!

Пожертвуйте сайту, мы стараемся ради вас.

Оглавление

Безопасность по умолчанию

Программа на Electron работает с правами пользователя: может читать и удалять его файлы. Если в окно попадёт чужой скрипт — из заметки, скопированной из интернета, или со страницы, открытой по ссылке, — он получит те же права. Настройки безопасности ограничивают страницу так, чтобы такой скрипт ничего не мог сделать.

Три настройки окна

Все три включены по умолчанию в актуальных версиях Electron. Явно их можно записать так:

// main.js
const win = new BrowserWindow({
  width: 900,
  height: 600,
  webPreferences: {
    preload: path.join(__dirname, 'preload.js'),
    contextIsolation: true,
    nodeIntegration: false,
    sandbox: true,
  },
});

Писать их не обязательно, поведение будет таким же. Явная запись полезна как напоминание тем, кто будет читать код.

НастройкаЗначение по умолчаниюЧто делает
nodeIntegrationfalseСтраница не может вызвать require и использовать Node.js
contextIsolationtrueСкрипт preload и страница работают в разных мирах JavaScript
sandboxtrueПроцесс отрисовки ограничен системой, как вкладка Chrome

Изоляция контекста

С изоляцией у preload и у страницы разные объекты window. Страница не может подменить функцию, которую использует preload, и не может добраться до ipcRenderer. Она видит только то, что положено через contextBridge.exposeInMainWorld.

Content-Security-Policy

CSP — правило, откуда странице можно загружать скрипты, стили и картинки. Оно задаётся тегом meta в index.html:

<!-- index.html -->
<meta http-equiv="Content-Security-Policy"
      content="default-src 'self'; img-src 'self' data:">

'self' разрешает файлы только из папки программы, img-src дополнительно разрешает картинки в формате data:. Встроенные скрипты и чужие сайты запрещены: даже если в заметку попадёт тег script, он не выполнится.

Узкий мост

Через contextBridge передавайте отдельные функции с понятным назначением:

// preload.js
const { contextBridge, ipcRenderer } = require('electron');
 
contextBridge.exposeInMainWorld('notes', {
  save: (list) => ipcRenderer.invoke('notes:save', list),
});

Страница может только сохранить заметки. Если бы preload отдал весь ipcRenderer, страница могла бы отправить любое сообщение главному процессу.

Другие правила

  • Выводите пользовательский текст через textContent, а не innerHTML.
  • Не открывайте чужие сайты в окне со своим preload. Внешние ссылки открывайте в браузере через shell.openExternal(url) в главном процессе.
  • Обновляйте Electron: в новых версиях исправлены уязвимости Chromium.

Частая ошибка

Встретив ошибку require is not defined, новички ставят nodeIntegration: true и contextIsolation: false — так советуют старые статьи. После этого любой скрипт на странице получает полный доступ к компьютеру. Оставьте настройки по умолчанию и перенесите работу с Node.js в главный процесс, а странице дайте функцию через preload.

Спасибо, что читаете!

Пожертвуйте сайту, мы стараемся ради вас.