我希望这个答案能引起大家的注意,因为这里的大多数答案都会在您的电子应用程序中留下很大的安全漏洞。实际上,这个答案实际上就是您应该使用的答案require()
在电子应用程序中。(只有一个新的电子API使其在v7中更加干净)。
我使用最新的电子api在github上写了详细的解释/解决方案require()
,但是我将在这里简要解释为什么您应该使用预加载脚本,contextBridge和ipc。
问题
电子应用程序很棒,因为我们可以使用节点,但是这种能力是一把双刃剑。如果我们不小心的话,我们会给某人通过我们的应用程序访问节点的权限,并且由于该节点的行为不当,可能会损坏您的计算机或删除您的操作系统文件(我想还有其他事情)。
正如@raddevus在评论中提到的那样,在加载远程内容时这是必需的。如果您的电子应用完全离线 / 本地运行,那么打开电源就可以了。但是,我仍然会选择继续为使用您的应用程序的意外/恶意用户提供保障,并防止您机器上可能安装的任何可能的恶意软件与您的电子应用程序进行交互并使用攻击媒介(这非常少见) ,但可能会发生)!nodeIntegration:true
nodeIntegration:false
nodeIntegration:true
问题是什么样的
当您(以下任何一项)时,就会出现此问题:
- 已
nodeIntegration:true
启用
- 使用
remote
模块
所有这些问题使您可以从渲染器进程不间断地访问节点。如果渲染器进程被劫持,则可以认为所有丢失。
我们的解决方案是
解决方案是不给渲染器直接访问节点(即require()
),而是给我们的电子主进程访问require
,并且在我们的渲染器进程需要使用任何时间时require
,将请求封送给主进程。
在Electron的最新版本(7+)中,此方法的工作方式是在渲染器端设置ipcRenderer绑定,在主端设置ipcMain绑定。在ipcMain绑定中,我们设置了使用模块we的侦听器方法require()
。这很好,因为我们的主要流程可以满足require
所有需求。
我们使用contextBridge将ipcRenderer绑定传递给我们的应用程序代码(以便使用),因此,当我们的应用程序需要require
在main中使用d模块时,它会通过IPC(进程间通信)发送一条消息,并且主进程运行一些代码,然后我们将结果发送回一条消息。
大致来说,这就是您想要做的。
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>
免责声明
我是的作者secure-electron-template
,该模板是构建电子应用程序的安全模板。我关心这个主题,并且已经为此工作了几个星期(在这个时间点上)。