Que*_*ire 10 javascript security node.js electron
要加载远程内容时提供安全的适当水平,更说明一个BrowserWindow的contextIsolation和nodeIntegration选项必须启用和禁用分别。在这种情况下,主渲染器进程将无法使用 Node/Electron API。为了公开特定功能,窗口的预加载脚本可能会利用 Electron 的contextBridge功能,为主渲染器提供对选定 Node/Electron API 的访问权限。
尽管 Electron 文档中提供了信息,contextBridge但总体上缺乏具体的使用示例。一般来说,现有的文档/教程在实现 Electron 应用程序时并不关注采用安全实践。
以下是contextBridge我设法在网上找到的一个使用示例:https : //github.com/reZach/secure-electron-template
您能否提供额外的资源/示例,这些资源/示例可能对实现安全的 Electron 应用程序(依赖于contextBridge功能)有用?
对contextBridge最佳实践的洞察力也受到高度赞赏。
dre*_*mLo 11
以下是all设置步骤
index.js文件中,指向BrowserWindow配置对象中的预加载脚本:new BrowserWindow({
webPreferences: {
preload: path.resolve(app.getAppPath(), 'preload.js')
}
})Run Code Online (Sandbox Code Playgroud)
preload.js位于您的应用程序文件夹中。我使用 webpack 来编译所有内容,因此该preload.js文件不是我的 js 代码所在的位置,而是位于electron-builder将编译可执行文件的 app 目录中。以下是其中preload.js包含的内容:process.once('loaded', () => {
const { contextBridge, ipcRenderer, shell } = require('electron')
contextBridge.exposeInMainWorld('electron', {
on (eventName, callback) {
ipcRenderer.on(eventName, callback)
},
async invoke (eventName, ...params) {
return await ipcRenderer.invoke(eventName, ...params)
},
async shellOpenExternal (url) {
await shell.openExternal(url)
},
async shellOpenPath (file) {
await shell.openPath(file)
},
async shellTrashItem (file) {
await shell.trashItem(file)
}
})
})Run Code Online (Sandbox Code Playgroud)
window.electron.on('nodeJSEvent', (event, param1, param2) => {
console.log('nodeJSEvent has been called with params', param1, param2)
})
const foo = await window.electron.invoke('nodeJSEvent', param1, param2)
console.log(foo)
await window.electron.shellOpenExternal(url)
await window.electron.shellOpenPath(file)
await window.electron.shellTrashItem(file)Run Code Online (Sandbox Code Playgroud)
就是这样。所有这些代码都是为了让客户端代码能够调用我们在预加载脚本中定义的 Nodejs 代码。
我是模板的作者,让我提供一些您可能会觉得有用的背景知识。免责声明:我不是安全研究人员,但这是从多个来源抓取的。
ContextBridge很重要,因为它提供了防止将值传递到基于旧方式的渲染器进程的保护。
老办法
const {
ipcRenderer
} = require("electron");
window.send = function(channel, data){
// whitelist channels
let validChannels = ["toMain"];
if (validChannels.includes(channel)) {
ipcRenderer.send(channel, data);
}
};
window.recieve = function(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)
此代码很容易受到客户端开放开发工具和修改的功能定义window.send和window.recieve。然后客户端可以开始在main.js脚本中ping 您的 ipcMain ,然后可能会造成一些损害,因为它们可以绕过预加载 js 中的白名单 ipc 通道。这假设您也在 main.js 中加入了白名单,但我已经看到很多示例,这些示例没有并且很容易受到这种攻击。
从文档:
函数值被代理到另一个上下文,所有其他值都被复制和冻结。API 对象中发送的任何数据/原语都变得不可变,并且桥任一侧的更新不会导致另一侧的更新。
换句话说,因为我们使用contextBridge.exposeInMainWorld,我们的渲染器进程无法修改我们公开的函数的定义,从而保护我们免受可能的安全攻击向量。
新方法
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)
小智 3
我自己也遇到了一些麻烦。我的解决方案是这个 preload.js 模板
const { ipcRenderer, contextBridge } = require('electron')
const validChannels = ["toMain", "myRenderChannel"];
contextBridge.exposeInMainWorld(
"api", {
send: (channel, data) => {
if (validChannels.includes(channel)) {
ipcRenderer.send(channel, data);
}
},
on: (channel, callback) => {
if (validChannels.includes(channel)) {
// Filtering the event param from ipcRenderer
const newCallback = (_, data) => callback(data);
ipcRenderer.on(channel, newCallback);
}
},
once: (channel, callback) => {
if (validChannels.includes(channel)) {
const newCallback = (_, data) => callback(data);
ipcRenderer.once(channel, newCallback);
}
},
removeListener: (channel, callback) => {
if (validChannels.includes(channel)) {
ipcRenderer.removeListener(channel, callback);
}
},
removeAllListeners: (channel) => {
if (validChannels.includes(channel)) {
ipcRenderer.removeAllListeners(channel)
}
},
}
);
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
8011 次 |
| 最近记录: |