Espero que esta respuesta reciba algo de atención, porque la gran mayoría de las respuestas aquí dejan grandes agujeros de seguridad en su aplicación electrónica. De hecho, esta respuesta es esencialmente lo que debería estar haciendo para usar require()
en sus aplicaciones electrónicas. (Solo hay una nueva API de electrones que lo hace un poco más limpio en la v7).
Escribí una explicación / solución detallada en github usando las apis electrónicas más actuales de cómo puedes hacer require()
algo, pero explicaré brevemente aquí por qué deberías seguir un enfoque utilizando un script de precarga, contextBridge e ipc.
El problema
Las aplicaciones electrónicas son geniales porque podemos usar el nodo, pero este poder es un arma de doble filo. Si no tenemos cuidado, le damos acceso a alguien al nodo a través de nuestra aplicación, y con el nodo un mal actor puede dañar su máquina o borrar los archivos de su sistema operativo (entre otras cosas, me imagino).
Como lo mencionó @raddevus en un comentario, esto es necesario al cargar contenido remoto . Si su aplicación electrónica está completamente fuera de línea / local , entonces probablemente esté bien simplemente encendiéndola nodeIntegration:true
. Sin embargo, todavía optaría por seguir nodeIntegration:false
actuando como una protección para los usuarios accidentales / maliciosos que usan su aplicación, y evitar que cualquier posible malware que pueda instalarse en su máquina interactúe con su aplicación electron y use el nodeIntegration:true
vector de ataque (increíblemente raro , pero podría suceder)!
Como se ve el problema
Este problema se manifiesta cuando usted (cualquiera de los siguientes):
- Tener
nodeIntegration:true
habilitado
- Usa el
remote
módulo
Todos estos problemas dan acceso ininterrumpido al nodo desde su proceso de renderizado. Si su proceso de renderizado alguna vez es secuestrado, puede considerar que todo está perdido.
Cual es nuestra solucion
La solución es no darle al renderizador acceso directo al nodo (es decir require()
), sino darle a nuestro proceso principal de electrones acceso a require
, y cada vez que nuestro proceso renderizador necesite usar require
, dirigir una solicitud al proceso principal.
La forma en que esto funciona en las últimas versiones (7+) de Electron es en el lado del renderizador, configuramos los enlaces ipcRenderer , y en el lado principal configuramos los enlaces ipcMain . En los enlaces ipcMain configuramos métodos de escucha que usan módulos we require()
. Esto está muy bien porque nuestro proceso principal puede hacer require
todo lo que quiera.
Usamos el contextBridge para pasar los enlaces de ipcRenderer al código de nuestra aplicación (para usar), por lo que cuando nuestra aplicación necesita usar los require
módulos d en main, envía un mensaje a través de IPC (comunicación entre procesos) y el proceso principal se ejecuta algún código, y luego enviamos un mensaje con nuestro resultado.
A grandes rasgos , esto es lo que quiere hacer.
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>
Descargo de responsabilidad
Soy el autor de secure-electron-template
una plantilla segura para crear aplicaciones electrónicas. Me importa este tema y he estado trabajando en esto durante algunas semanas (en este momento).