Mar*_*van 64 html javascript node.js electron
我正在为自己的目的创建Electron应用程序。我的问题是,当我在HTML页面中使用节点函数时,它引发以下错误:
未定义'require()'。
有没有办法在我所有的HTML页面中使用Node功能?如果可能的话,请给我举个例子,或者提供一个链接。以下是我要在HTML页面中尝试使用的变量:
var app = require('electron').remote;
var dialog = app.dialog;
var fs = require('fs');
Run Code Online (Sandbox Code Playgroud)
这些是我在Electron的所有HTML窗口中使用的值。
Sat*_*esh 164
从版本5开始,默认值nodeIntegration
从true更改为false。您可以在创建浏览器窗口时启用它:
app.on('ready', () => {
mainWindow = new BrowserWindow({
webPreferences: {
nodeIntegration: true
}
});
});
Run Code Online (Sandbox Code Playgroud)
Zac*_*Zac 76
我希望这个答案得到一些关注,因为这里的大部分答案都会在您的电子应用程序中留下很大的安全漏洞。事实上,这个答案本质上就是你应该require()
在你的电子应用程序中使用的东西。(只有一个新的电子 API 使它在 v7 中更简洁一些)。
我使用最新的电子 API 在 github 中写了一个详细的解释/解决方案require()
,但我将在这里简要解释为什么你应该遵循使用预加载脚本、contextBridge 和 ipc 的方法。
Electron 应用程序很棒,因为我们可以使用 node,但这种能力是一把双刃剑。如果我们不小心,我们会通过我们的应用程序授予某人访问节点的权限,而对于节点,坏人可能会损坏您的机器或删除您的操作系统文件(除其他外,我想)。
正如@raddevus 在评论中提出的,这在加载远程内容时是必要的。如果您的电子应用程序完全离线/本地,那么您可能只需打开即可。但是,我仍然会选择继续为使用您的应用程序的意外/恶意用户提供保护,并防止任何可能安装在您机器上的恶意软件与您的电子应用程序交互并使用攻击向量(非常罕见) ,但可能会发生)!
nodeIntegration:true
nodeIntegration:false
nodeIntegration:true
当您(以下任何一项)时,就会出现此问题:
nodeIntegration:true
启用remote
模块所有这些问题都使您可以从渲染器进程不间断地访问节点。如果您的渲染器进程被劫持,您可以认为一切都已丢失。
解决方案是不让渲染器直接访问节点(即require()
),而是让我们的电子主进程访问require
,并且在我们的渲染器进程需要使用的任何时候require
,将请求编组到主进程。
在最新版本(7+)Electron 中的工作方式是在渲染器端设置ipcRenderer绑定,在主端设置ipcMain绑定。在 ipcMain 绑定中,我们设置了使用模块的侦听器方法require()
。这很好,因为我们的主进程可以require
随心所欲。
我们使用contextBridge将 ipcRenderer 绑定传递给我们的应用程序代码(使用),因此当我们的应用程序需要使用require
main 中的d 模块时,它通过 IPC(进程间通信)发送消息并运行主进程一些代码,然后我们发送一条带有结果的消息。
粗略地说,这就是你想要做的。
主文件
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);
});
});
Run Code Online (Sandbox Code Playgroud)
预加载.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));
}
}
}
);
Run Code Online (Sandbox Code Playgroud)
索引.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>
Run Code Online (Sandbox Code Playgroud)
我是secure-electron-template
构建电子应用程序的安全模板的作者。我关心这个话题,并且已经为此工作了几个星期(此时此刻)。
小智 11
出于安全原因,您应该保留nodeIntegration: false
并使用预加载脚本,以通过window变量将需要的Node / Electron API暴露给渲染器进程(视图)。从电子文档:
预加载脚本仍然可以访问
require
和其他Node.js功能
main.js
const mainWindow = new BrowserWindow({
webPreferences: {
preload: path.join(app.getAppPath(), 'preload.js')
}
})
Run Code Online (Sandbox Code Playgroud)
preload.js
const { remote } = require('electron');
let currWindow = remote.BrowserWindow.getFocusedWindow();
window.closeCurrentWindow = function(){
currWindow.close();
}
Run Code Online (Sandbox Code Playgroud)
renderer.js
let closebtn = document.getElementById('closebtn');
closebtn.addEventListener('click', (e) => {
e.preventDefault();
window.closeCurrentWindow();
});
Run Code Online (Sandbox Code Playgroud)
首先,@Sathiraumesh 解决方案让您的电子应用程序面临巨大的安全问题。想象一下,您的应用程序正在向 中添加一些额外的功能messenger.com
,例如,当您有未读消息时,工具栏的图标会发生变化或闪烁。所以在你的main.js
文件中,你像这样创建新的 BrowserWindow(注意我故意拼错了 messenger.com):
app.on('ready', () => {
const mainWindow = new BrowserWindow({
webPreferences: {
nodeIntegration: true
}
});
mainWindow.loadURL(`https://messengre.com`);
});
Run Code Online (Sandbox Code Playgroud)
如果messengre.com
是一个恶意网站,想要损害您的计算机怎么办。如果您设置nodeIntegration: true
此站点可以访问您的本地文件系统并可以执行以下操作:
require('child_process').exec('rm -r ~/');
Run Code Online (Sandbox Code Playgroud)
你的主目录不见了。
解决方案
只公开你需要的东西,而不是一切。这是通过使用require
语句预加载 javascript 代码来实现的。
// main.js
app.on('ready', () => {
const mainWindow = new BrowserWindow({
webPreferences: {
preload: `${__dirname}/preload.js`
}
});
mainWindow.loadURL(`https://messengre.com`);
});
Run Code Online (Sandbox Code Playgroud)
// preload.js
window.ipcRenderer = require('electron').ipcRenderer;
Run Code Online (Sandbox Code Playgroud)
// index.html
<script>
window.ipcRenderer.send('channel', data);
</script>
Run Code Online (Sandbox Code Playgroud)
现在可怕的messengre.com
无法删除您的整个文件系统。
您正在使用nodeIntegration: false
BrowserWindow初始化时吗?如果是这样,请将其设置为true
(默认值为true
)。
并在HTML中包含您的外部脚本,而不是这样<script> src="./index.js" </script>
:
<script>
require('./index.js')
</script>
Run Code Online (Sandbox Code Playgroud)
看起来 Electron 的安全性是这样演变的(来源)。
Electron 1 nodeIntegration默认为 true
Renderer 拥有对 Node API 的完全访问权——如果 Renderer 加载远程代码,则存在巨大的安全风险。
Electron 5 nodeIntegration默认为 false
当设置为 false 时,预加载脚本用于向渲染器公开特定的 API。(无论nodeIntegration的值如何,预加载脚本始终可以访问节点 API )
//preload.js
window.api = {
deleteFile: f => require('fs').unlink(f)
}
Run Code Online (Sandbox Code Playgroud)
Electron 5 contextIsolation默认为 true(实际上在 Electron 11 中仍然默认为 false)
这会导致预加载脚本在单独的上下文中运行。你不能再这样做了window.api = ...
。你现在必须做:
//preload.js
const { contextBridge } = require('electron')
contextBridge.exposeInMainWorld('api', {
deleteFile: f => require('fs').unlink(f)
})
Run Code Online (Sandbox Code Playgroud)
require()
沙盒渲染器中的Electron 6 ing 节点内置程序不再隐式加载远程版本
如果 Renderer 已sandbox
设置为 true,则必须执行以下操作:
//preload.js
const { contextBridge, remote } = require('electron')
contextBridge.exposeInMainWorld('api', {
deleteFile: f => remote.require('fs').unlink(f)
})
Run Code Online (Sandbox Code Playgroud)
Electron 10 enableRemoteModule默认为 false(远程模块在 Electron 12 中已弃用)
remote
当您需要从沙盒渲染器访问 Node API 时使用该模块(如上例所示);或者当您需要访问仅适用于主进程(例如对话框、菜单)的 Electron API 时。如果没有remote
,您将需要编写如下所示的显式 IPC 处理程序。
//preload.js
const { contextBridge, ipcRenderer } = require('electron')
contextBridge.exposeInMainWorld('api', {
displayMessage: text => ipcRenderer.invoke("displayMessage", text)
})
//main.js
const { ipcMain, dialog } = require('electron')
ipcMain.handle("displayMessage", text => dialog.showMessageBox(text))
Run Code Online (Sandbox Code Playgroud)
Electron 10 弃用 nodeIntegration标志(在 Electron 12 中删除)
始终设置{nodeIntegration: false, contextIsolation: true, enableRemoteModule: false}
。
为了最大的安全性,设置{sandbox: true}
. 您的预加载脚本将不得不使用 IPC 调用 Main 进程来完成所有工作。
如果sandbox
为 false,则您的预加载脚本可以直接访问 Node API,如require('fs').readFile
. 只要你不这样做,你就是安全的:
//bad
contextBridge.exposeInMainWorld('api', {
readFile: require('fs').readFile
})
Run Code Online (Sandbox Code Playgroud)
由于我正在遵循的教程,我想做的就是在我的 html 页面中需要一个 js 文件。但是,我打算使用远程模块,因此安全性至关重要。我修改了迈克尔的答案,所以我发帖,纯粹是为了那些像我一样花了几个小时寻找“要求”的安全替代方案的人。如果代码不正确,请随时指出。
main.js
const electron = require('electron');
const app=electron.app;
const BrowserWindow=electron.BrowserWindow;
const ipcMain=electron.ipcMain;
const path=require('path');
const url=require('url');
let win;
function createWindow(){
win=new BrowserWindow({
webPreferences:{
contextIsolation: true,
preload: path.join(__dirname, "preload.js")
}
});
win.loadURL(url.format({
pathname: path.join(__dirname, 'index.html'),
protocol: 'file',
slashes: true
}));
win.on('close', function(){
win=null
});
}
app.on('ready', createWindow);
Run Code Online (Sandbox Code Playgroud)
预加载.js
const electron=require('electron');
const contextBridge=electron.contextBridge;
contextBridge.exposeInMainWorld(
"api", {
loadscript(filename){
require(filename);
}
}
);
Run Code Online (Sandbox Code Playgroud)
索引.html
<!DOCTYPE html>
<html>
<head>
<title>Hello World App</title>
</head>
<body>
<h1>Hello World</h1>
<button id="btn">Click</button>
</body>
<script>
window.api.loadscript('./index.js');
</script>
</html>
Run Code Online (Sandbox Code Playgroud)
索引.js
const btn = document.getElementById('btn');
btn.addEventListener('click', function(){
console.log('button clicked');
});
Run Code Online (Sandbox Code Playgroud)
我特别想知道这是否仍然存在安全风险。谢谢。
小智 5
如果您只是不关心任何安全问题,并且希望JavaScript 在浏览器窗口上正确解释 require,那么可以在 main.js 代码上添加一个额外的标志:
webPreferences: {
nodeIntegration: true,
nodeIntegrationInWorker: true,
nodeIntegrationInSubFrames: true,
enableRemoteModule: true,
contextIsolation: false //required flag
}
//rest of the code...
Run Code Online (Sandbox Code Playgroud)
归档时间: |
|
查看次数: |
32460 次 |
最近记录: |