Espero que esta resposta receba alguma atenção, porque a grande maioria das respostas aqui deixa grandes brechas de segurança em seu aplicativo de elétrons. Na verdade, essa resposta é essencialmente o que você deve fazer para usar require()
em seus aplicativos de elétrons. (Existe apenas uma nova API de elétrons que o torna um pouco mais limpo na v7).
Eu escrevi uma explicação / solução detalhada no github usando as apis de elétrons mais atuais de como você pode fazer require()
algo, mas vou explicar brevemente aqui porque você deve seguir uma abordagem usando um script de pré-carregamento, contextBridge e ipc.
O problema
Aplicativos de elétrons são ótimos porque podemos usar o node, mas esse poder é uma faca de dois gumes. Se não tomarmos cuidado, damos a alguém acesso ao node por meio de nosso aplicativo, e com o node um malfeitor pode corromper sua máquina ou deletar seus arquivos de sistema operacional (entre outras coisas, imagino).
Conforme mencionado por @raddevus em um comentário, isso é necessário ao carregar conteúdo remoto . Se o seu aplicativo eletrônico é totalmente offline / local , provavelmente você não terá problemas simplesmente ligando-o nodeIntegration:true
. Ainda assim, optaria por continuar nodeIntegration:false
a agir como uma proteção para usuários acidentais / mal-intencionados que usam seu aplicativo e evitar que qualquer malware que possa ser instalado em sua máquina interaja com seu aplicativo de elétrons e use o nodeIntegration:true
vetor de ataque (incrivelmente raro , mas pode acontecer)!
Como é o problema?
Este problema se manifesta quando você (qualquer um dos abaixo):
- Ter
nodeIntegration:true
ativado
- Use o
remote
módulo
Todos esses problemas dão acesso ininterrupto ao nó do seu processo de renderização. Se o seu processo de renderização for sequestrado, você pode considerar que tudo está perdido.
Qual é a nossa solução
A solução é não dar ao renderizador acesso direto ao nó (isto é require()
), mas dar ao nosso processo principal de elétrons acesso require
, e a qualquer momento que nosso processo renderizador precisar usarrequire
, empacotar uma solicitação para o processo principal.
A maneira como isso funciona nas versões mais recentes (7 +) do Electron é no lado do renderizador que configuramos os vínculos ipcRenderer e, no lado principal, configuramos os vínculos ipcMain . Nas ligações ipcMain, configuramos métodos de escuta que usam módulos nós require()
. Isso é bom e bom porque nosso processo principal poderequire
tudo o que quiser.
Usamos o contextBridge para passar as ligações ipcRenderer para o código do nosso aplicativo (para usar) e, portanto, quando nosso aplicativo precisa usar os require
módulos d no principal, ele envia uma mensagem via IPC (comunicação entre processos) e o processo principal é executado algum código e, em seguida, enviamos uma mensagem de volta com nosso resultado.
Aproximadamente , aqui está o que você deseja fazer.
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>
aviso Legal
Sou o autor de secure-electron-template
, um modelo seguro para construir aplicativos de elétrons. Preocupo-me com este tópico e venho trabalhando nisso há algumas semanas (neste momento).