Я надеюсь, что этот ответ привлечет некоторое внимание, потому что подавляющее большинство ответов здесь оставляют большие дыры в безопасности в вашем электронном приложении. Фактически, этот ответ - это то, что вы должны делать, чтобы использовать require()
в своих электронных приложениях. (Есть только новый электронный API, который делает его немного чище в версии 7).
Я написал подробное объяснение / решение в github, используя самый последний электронный API того, как вы можете require()
что-то сделать, но я кратко объясню здесь, почему вы должны следовать подходу с использованием сценария предварительной загрузки, contextBridge и ipc.
Эта проблема
Приложения Electron великолепны, потому что мы можем использовать узел, но эта сила - палка о двух концах. Если мы не будем осторожны, мы дадим кому-то доступ к узлу через наше приложение, и с помощью узла злоумышленник может повредить вашу машину или удалить файлы вашей операционной системы (среди прочего, я полагаю).
Как отметил @raddevus в комментарии, это необходимо при загрузке удаленного контента. Если ваше электронное приложение полностью автономно / локально , вы, вероятно, можете просто включить его nodeIntegration:true
. Тем не менее, я бы по-прежнему предпочел nodeIntegration:false
действовать в качестве защиты от случайных / злонамеренных пользователей, использующих ваше приложение, и предотвращать взаимодействие любых возможных вредоносных программ, которые могут когда-либо устанавливаться на вашем компьютере, с вашим электронным приложением и использовать nodeIntegration:true
вектор атаки (невероятно редко , но могло случиться)!
Как выглядит проблема
Эта проблема проявляется, когда вы (любой из перечисленных ниже):
- Были ли
nodeIntegration:true
включен
- Используйте
remote
модуль
Все эти проблемы обеспечивают непрерывный доступ к узлу из вашего процесса рендеринга. Если ваш процесс рендеринга когда-либо будет взломан, вы можете считать, что все потеряно.
Какое у нас решение
Решение состоит в том, чтобы не давать рендереру прямой доступ к узлу (т. Е. require()
), А предоставить нашему электронному основному процессу доступ require
и в любое время, когда нашему процессу рендеринга потребуется его использовать require
, маршалировать запрос к основному процессу.
В последних версиях (7+) Electron это работает так: на стороне рендерера мы настраиваем привязки ipcRenderer , а на основной стороне мы настраиваем привязки ipcMain . В привязках ipcMain мы настраиваем методы прослушивателя, которые используют модули, которые мы require()
. Это нормально, потому что наш основной процесс может require
все, что угодно.
Мы используем contextBridge для передачи привязок ipcRenderer к коду нашего приложения (для использования), и поэтому, когда нашему приложению необходимо использовать require
модули d в основном, оно отправляет сообщение через IPC (межпроцессное взаимодействие) и запускается основной процесс. какой-то код, а затем мы отправляем сообщение с нашим результатом.
Примерно вот что вы хотите сделать.
main.js
const {
app,
BrowserWindow,
ipcMain
} = require("electron");
const path = require("path");
const fs = require("fs");
// Keep a global reference of the window object, if you don't, the window will
// be closed automatically when the JavaScript object is garbage collected.
let win;
async function createWindow() {
// Create the browser window.
win = new BrowserWindow({
width: 800,
height: 600,
webPreferences: {
nodeIntegration: false, // is default value after Electron v5
contextIsolation: true, // protect against prototype pollution
enableRemoteModule: false, // turn off remote
preload: path.join(__dirname, "preload.js") // use a preload script
}
});
// Load app
win.loadFile(path.join(__dirname, "dist/index.html"));
// rest of code..
}
app.on("ready", createWindow);
ipcMain.on("toMain", (event, args) => {
fs.readFile("path/to/file", (error, data) => {
// Do something with file contents
// Send result back to renderer process
win.webContents.send("fromMain", responseObj);
});
});
preload.js
const {
contextBridge,
ipcRenderer
} = require("electron");
// Expose protected methods that allow the renderer process to use
// the ipcRenderer without exposing the entire object
contextBridge.exposeInMainWorld(
"api", {
send: (channel, data) => {
// whitelist channels
let validChannels = ["toMain"];
if (validChannels.includes(channel)) {
ipcRenderer.send(channel, data);
}
},
receive: (channel, func) => {
let validChannels = ["fromMain"];
if (validChannels.includes(channel)) {
// Deliberately strip event as it includes `sender`
ipcRenderer.on(channel, (event, ...args) => func(...args));
}
}
}
);
index.html
<!doctype html>
<html lang="en-US">
<head>
<meta charset="utf-8"/>
<title>Title</title>
</head>
<body>
<script>
window.api.receive("fromMain", (data) => {
console.log(`Received ${data} from main process`);
});
window.api.send("toMain", "some data");
</script>
</body>
</html>
Отказ от ответственности
Я автор secure-electron-template
безопасного шаблона для создания электронных приложений. Мне небезразлична эта тема, и я работаю над ней несколько недель (на данный момент).