Ich hoffe, dass diese Antwort einige Aufmerksamkeit erhält, da eine große Mehrheit der Antworten hier große Sicherheitslücken in Ihrer Elektronen-App hinterlässt . Tatsächlich ist diese Antwort im Wesentlichen das, was Sie tun sollten, um sie require()
in Ihren Elektronen-Apps zu verwenden. (Es gibt nur eine neue Elektronen-API, die es in Version 7 ein bisschen sauberer macht).
Ich habe eine ausführliche Erklärung / Lösung in Github unter Verwendung der aktuellsten Elektronen-Apis geschrieben, wie Sie require()
etwas können, aber ich werde hier kurz erklären, warum Sie einen Ansatz mit einem Preload-Skript, contextBridge und ipc verfolgen sollten.
Das Problem
Elektronen-Apps sind großartig, weil wir Node verwenden können, aber diese Kraft ist ein zweischneidiges Schwert. Wenn wir nicht aufpassen, geben wir jemandem über unsere App Zugriff auf den Knoten, und mit dem Knoten kann ein schlechter Akteur Ihren Computer beschädigen oder Ihre Betriebssystemdateien löschen (unter anderem, wie ich mir vorstellen kann).
Wie von @raddevus in einem Kommentar erwähnt, ist dies beim Laden von Remote- Inhalten erforderlich . Wenn Ihre Elektronen-App vollständig offline / lokal ist , können Sie sie wahrscheinlich einfach einschalten nodeIntegration:true
. Ich würde mich jedoch weiterhin dafür entscheiden, weiterhin nodeIntegration:false
als Schutz für versehentliche / böswillige Benutzer zu fungieren, die Ihre App verwenden, und zu verhindern, dass mögliche Malware, die jemals auf Ihrem Computer installiert wird, mit Ihrer Elektronen-App interagiert und den nodeIntegration:true
Angriffsvektor verwendet (unglaublich selten) , könnte aber passieren)!
Wie sieht das Problem aus?
Dieses Problem tritt auf, wenn Sie (eines der folgenden):
- haben
nodeIntegration:true
aktiviert
- Verwenden Sie das
remote
Modul
All diese Probleme ermöglichen einen ununterbrochenen Zugriff auf den Knoten von Ihrem Renderer-Prozess. Wenn Ihr Renderer-Prozess jemals entführt wird, können Sie davon ausgehen, dass alles verloren ist.
Was ist unsere Lösung?
Die Lösung besteht darin, dem Renderer keinen direkten Zugriff auf den Knoten (dh require()
) zu gewähren, sondern unserem Elektronenhauptprozess Zugriff auf eine Anforderung an den Hauptprozess zu gewähren require
, und wann immer unser Rendererprozess diese verwenden muss require
.
Dies funktioniert in den neuesten Versionen (7+) von Electron auf der Rendererseite, auf der wir ipcRenderer- Bindungen einrichten , und auf der Hauptseite richten wir ipcMain- Bindungen ein. In den ipcMain-Bindungen richten wir Listener-Methoden ein, die von uns verwendete Module verwenden require()
. Das ist in Ordnung und gut, weil unser Hauptprozess require
alles kann, was er will.
Wir verwenden die contextBridge , um die ipcRenderer-Bindungen an unseren App-Code zu übergeben (zu verwenden). Wenn unsere App also die require
d-Module in main verwenden muss, sendet sie eine Nachricht über IPC (Inter-Process-Communication) und der Hauptprozess wird ausgeführt etwas Code, und wir senden dann eine Nachricht mit unserem Ergebnis zurück.
Hier ist ungefähr das , was Sie tun möchten.
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);
});
});
pretoad.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>
Haftungsausschluss
Ich bin der Autor secure-electron-template
einer sicheren Vorlage zum Erstellen von Elektronen-Apps. Ich interessiere mich für dieses Thema und arbeite seit einigen Wochen (zu diesem Zeitpunkt) daran.