Mam nadzieję, że ta odpowiedź przyciągnie trochę uwagi, ponieważ znaczna większość odpowiedzi pozostawia duże luki w zabezpieczeniach aplikacji electron. W rzeczywistości ta odpowiedź jest zasadniczo tym, co powinieneś zrobić, aby użyć require()
w swoich aplikacjach electron. (Jest tylko nowe elektroniczne API, które sprawia, że jest trochę czystsze w wersji 7).
Napisałem szczegółowe wyjaśnienie / rozwiązanie na githubie, używając najbardziej aktualnych elektronicznych require()
interfejsów API, jak można coś zrobić, ale wyjaśnię tutaj pokrótce, dlaczego powinieneś zastosować podejście przy użyciu skryptu wstępnego ładowania, contextBridge i ipc.
Problem
Aplikacje Electron są świetne, ponieważ używamy węzła, ale ta moc jest mieczem obosiecznym. Jeśli nie jesteśmy ostrożni, dajemy komuś dostęp do węzła za pośrednictwem naszej aplikacji, a przy użyciu węzła zły aktor może uszkodzić twój komputer lub usunąć pliki systemu operacyjnego (między innymi, jak mi się wydaje).
Jak wspomniał @raddevus w komentarzu, jest to konieczne podczas ładowania zdalnej zawartości. Jeśli Twoja aplikacja electron jest całkowicie offline / lokalna , prawdopodobnie wystarczy, że ją włączysz nodeIntegration:true
. Nadal jednak zdecydowałbym się zachować nodeIntegration:false
ochronę przed przypadkowymi / złośliwymi użytkownikami korzystającymi z Twojej aplikacji i zapobiegać wszelkim możliwym złośliwym programom, które mogłoby kiedykolwiek zostać zainstalowane na Twoim komputerze, przed interakcją z Twoją aplikacją Electron i wykorzystaniem nodeIntegration:true
wektora ataku (niezwykle rzadkie , ale może się zdarzyć)!
Jak wygląda problem
Ten problem objawia się, gdy (którykolwiek z poniższych):
- zostały
nodeIntegration:true
włączone
- Skorzystaj z
remote
modułu
Wszystkie te problemy zapewniają nieprzerwany dostęp do węzła z procesu renderowania. Jeśli twój proces renderowania zostanie kiedykolwiek przejęty, możesz uznać, że wszystko jest stracone.
Jakie jest nasze rozwiązanie
Rozwiązaniem jest, aby nie dawać rendererowi bezpośredniego dostępu do węzła (tj. require()
), Ale dać naszemu głównemu procesowi Electronowi dostęp do require
, i za każdym razem, gdy nasz proces renderowania potrzebuje require
, zorganizować żądanie do głównego procesu.
Sposób, w jaki to działa w najnowszych wersjach (7+) Electron, polega na tym, że po stronie renderera skonfigurowaliśmy wiązania ipcRenderer , a po stronie głównej skonfigurowaliśmy wiązania ipcMain . W powiązaniach ipcMain konfigurujemy metody nasłuchujące, które używają modułów my require()
. Jest to w porządku i dobrze, ponieważ nasz główny proces może zrobić require
wszystko, czego chce.
Używamy contextBridge do przekazywania powiązań ipcRenderer do naszego kodu aplikacji (do użycia), a więc gdy nasza aplikacja musi użyć require
modułów d w main, wysyła wiadomość za pośrednictwem IPC (komunikacja między procesami) i proces główny działa trochę kodu, a następnie wysyłamy wiadomość zwrotną z naszym wynikiem.
Z grubsza , oto co chcesz zrobić.
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>
Zrzeczenie się
Jestem autorem secure-electron-template
bezpiecznego szablonu do tworzenia aplikacji Electron. Zależy mi na tym temacie i pracuję nad tym od kilku tygodni (w tym momencie).