Spero che questa risposta riceva una certa attenzione, perché la grande maggioranza delle risposte qui lascia grandi buchi di sicurezza nella tua app elettronica. In effetti questa risposta è essenzialmente ciò che dovresti fare per usarla require()
nelle tue app di elettroni. (C'è solo una nuova API elettronica che lo rende un po 'più pulito nella v7).
Ho scritto una spiegazione / soluzione dettagliata in GitHub usando le API elettroniche più recenti di come puoi fare require()
qualcosa, ma spiegherò brevemente qui perché dovresti seguire un approccio usando uno script di precaricamento, contextBridge e ipc.
Il problema
Le app di Electron sono fantastiche perché possiamo usare il nodo, ma questo potere è un'arma a doppio taglio. Se non stiamo attenti, diamo a qualcuno l'accesso a node tramite la nostra app, e con node un cattivo attore può corrompere la tua macchina o cancellare i file del tuo sistema operativo (tra le altre cose, immagino).
Come indicato da @raddevus in un commento, questo è necessario quando si caricano contenuti remoti . Se la tua app elettronica è completamente offline / locale , probabilmente stai semplicemente accendendola nodeIntegration:true
. Tuttavia, sceglierei comunque di continuare nodeIntegration:false
a fungere da salvaguardia per utenti accidentali / malintenzionati che utilizzano la tua app e impedire a qualsiasi possibile malware che potrebbe essere installato sulla tua macchina di interagire con la tua app di elettroni e utilizzare il nodeIntegration:true
vettore di attacco (incredibilmente raro , ma potrebbe succedere)!
Che aspetto ha il problema
Questo problema si manifesta quando tu (uno qualsiasi dei seguenti):
- Hanno
nodeIntegration:true
abilitato
- Usa il
remote
modulo
Tutti questi problemi danno accesso ininterrotto al nodo dal processo di rendering. Se il processo di rendering viene mai dirottato, puoi considerare che tutto è perduto.
Qual è la nostra soluzione
La soluzione è di non dare al renderer l' accesso diretto al nodo (cioè require()
), ma di dare al nostro processo principale di electron l'accesso a require
, e ogni volta che il nostro processo di rendering ha bisogno di usare require
, marshalling una richiesta al processo principale.
Il modo in cui funziona nelle ultime versioni (7+) di Electron è che sul lato renderer abbiamo impostato i collegamenti ipcRenderer e sul lato principale abbiamo impostato i collegamenti ipcMain . Nelle associazioni ipcMain impostiamo metodi di ascolto che utilizzano i moduli che noi require()
. Questo va bene e va bene perché il nostro processo principale può require
tutto ciò che vuole.
Usiamo il contextBridge per passare le associazioni ipcRenderer al codice della nostra app (da usare), e così quando la nostra app deve usare i require
moduli d in main, invia un messaggio tramite IPC (inter-process-communication) e il processo principale viene eseguito un po 'di codice, quindi inviamo un messaggio con il nostro risultato.
Approssimativamente , ecco cosa vuoi fare.
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>
Disclaimer
Sono l'autore di secure-electron-template
, un modello sicuro per creare app di elettroni. Ci tengo a questo argomento e ci sto lavorando da alcune settimane (in questo momento).