J'espère que cette réponse retiendra l'attention, car une grande majorité des réponses ici laissent de grandes failles de sécurité dans votre application électronique. En fait, cette réponse est essentiellement ce que vous devriez faire pour l'utiliser require()
dans vos applications électroniques. (Il y a juste une nouvelle API électronique qui le rend un peu plus propre dans la v7).
J'ai écrit une explication / solution détaillée dans github en utilisant les API électroniques les plus récentes sur la façon dont vous pouvez require()
quelque chose, mais je vais expliquer brièvement ici pourquoi vous devriez suivre une approche utilisant un script de préchargement, contextBridge et ipc.
Le problème
Les applications Electron sont excellentes parce que nous utilisons le nœud, mais ce pouvoir est une épée à double tranchant. Si nous ne faisons pas attention, nous donnons à quelqu'un l'accès au node via notre application, et avec node un mauvais acteur peut corrompre votre machine ou supprimer les fichiers de votre système d'exploitation (entre autres, j'imagine).
Comme indiqué par @raddevus dans un commentaire, cela est nécessaire lors du chargement de contenu distant . Si votre application électronique est entièrement hors ligne / locale , vous pouvez probablement simplement l'allumer nodeIntegration:true
. Cependant, je choisirais toujours de continuer nodeIntegration:false
à agir comme une protection pour les utilisateurs accidentels / malveillants utilisant votre application, et d'empêcher tout logiciel malveillant qui pourrait être installé sur votre machine d'interagir avec votre application électronique et d'utiliser le nodeIntegration:true
vecteur d'attaque (incroyablement rare , mais cela pourrait arriver)!
À quoi ressemble le problème
Ce problème se manifeste lorsque vous (l'un des éléments ci-dessous):
- Ont
nodeIntegration:true
activé
- Utilisez le
remote
module
Tous ces problèmes donnent un accès ininterrompu au nœud à partir de votre processus de rendu. Si votre processus de rendu est piraté, vous pouvez considérer que tout est perdu.
Quelle est notre solution
La solution est de ne pas donner au moteur de rendu un accès direct au nœud (c'est-à-dire require()
), mais de donner à notre processus électronique l'accès au processus principal require
, et à chaque fois que notre processus de rendu doit utiliser require
, rassembler une requête vers le processus principal.
La façon dont cela fonctionne dans les dernières versions (7+) d'Electron est du côté du rendu, nous configurons les liaisons ipcRenderer , et du côté principal, nous configurons les liaisons ipcMain . Dans les liaisons ipcMain, nous mettons en place des méthodes d'écoute qui utilisent les modules we require()
. C'est très bien parce que notre processus principal peut require
tout ce qu'il veut.
Nous utilisons le contextBridge pour transmettre les liaisons ipcRenderer au code de notre application (à utiliser), et donc, lorsque notre application doit utiliser les require
modules d dans main, elle envoie un message via IPC (communication inter-processus) et le processus principal s'exécute un peu de code, puis nous renvoyons un message avec notre résultat.
En gros , voici ce que vous voulez faire.
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>
Avertissement
Je suis l'auteur d' secure-electron-template
un modèle sécurisé pour créer des applications électroniques. Je me soucie de ce sujet et j'y travaille depuis quelques semaines (en ce moment).