P粉6158297422023-08-28 19:14:20
並非官方文件中的所有內容都可以在程式碼中的任何位置直接實現。您必須需要對環境和流程有簡潔的了解。
環境/流程 | 描述 |
---|---|
主要 | API 更接近作業系統(低階)。其中包括檔案系統、基於作業系統的通知彈出視窗、工作列等。這些是透過 Electron 的核心 API 和 Node.js 的組合實現的 |
預先載入 | 最近的附錄,以防止主環境中可用的強大 API 洩漏。有關更多詳細信息,請參閱 Electron v12 變更日誌< /a> 和問題#23506。 |
渲染器 | 現代 Web 瀏覽器的 API,例如 DOM 和前端 JavaScript(進階)。這是透過 Chromium 實現的。 |
場景 | contextIsolation | #nodeIntegration | #備註 |
---|---|---|---|
A | 假 |
假 |
不需要預先載入。 Node.js 在 Main 中可用,但在 Renderer 中不可用。 |
B | 假 |
true |
不需要預先載入。 Node.js 在 Main 和 Renderer 中可用。 |
C | true |
假 |
需要預先載入。 Node.js 在主載入和預先載入中可用,但在渲染器中不可用。 預設. 推薦。 |
D | true |
true |
需要預先載入。 Node.js 在 Main、Preload 和 Renderer 中可用。 |
您必須使用 Electron 的進程間通訊 (IPC) 才能使主進程和渲染進程進行通訊。
BrowserWindow.webContents.send() code>
傳送訊息給渲染器的方法ipcMain.handle()
從渲染器接收訊息的方法主要
/** * Sending messages to Renderer * `window` is an object which is an instance of `BrowserWindow` * `data` can be a boolean, number, string, object, or array */ window.webContents.send( 'custom-endpoint', data ); /** * Receiving messages from Renderer */ ipcMain.handle( 'custom-endpoint', async ( event, data ) => { console.log( data ) } )
預先載入
const { contextBridge, ipcRenderer } = require('electron') contextBridge.exposeInMainWorld( 'api', { send: ( channel, data ) => ipcRenderer.invoke( channel, data ), handle: ( channel, callable, event, data ) => ipcRenderer.on( channel, callable( event, data ) ) } )
渲染器
#/** * Sending messages to Main * `data` can be a boolean, number, string, object, or array */ api.send( 'custom-endpoint', data ) /** * Receiving messages from Main */ api.handle( 'custom-endpoint', ( event, data ) => function( event, data ) { console.log( data ) }, event);
盡可能遵守對相同流程/環境的承諾。您在 main 上的承諾應該保留在 main 上。您對渲染器的承諾也應該保留在渲染器上。不要做出從主程式跳到預先載入程式再到渲染器的承諾。
您的大部分業務邏輯仍應位於主端或渲染器端,但絕不應位於預先載入中。這是因為預載幾乎只是作為一種媒介而存在。預載應該非常小。
在OP的情況下, fs
應該在主端實作。
P粉3233748782023-08-28 11:18:17
我已經發表了一篇關於Electron 歷史的較大文章( Electron 版本中的安全性如何發生變化)以及Electron 開發人員可以採取的其他安全注意事項,以確保在新應用程式中正確使用預先載入檔案。
正如另一位用戶所問,讓我在下面解釋我的答案。
在 Electron 中使用 preload.js
的正確方法是在您的應用程式可能需要 require
的任何模組周圍公開白名單包裝器。
從安全性角度來看,公開require
或透過preload.js
中的require
呼叫檢索的任何內容都是危險的(請參閱< a href="https://github.com/electron/ Electron/issues/9920#issuecomment-575839738" rel="noreferrer">我的評論以獲取更多解釋原因)。如果您的應用程式載入遠端內容(許多應用程式都會這樣做),則尤其如此。
為了正確執行操作,您需要在 < 上启用许多选项code>BrowserWindow 正如我在下面詳細介紹的。設定這些選項會強制您的電子應用程式透過 IPC(進程間通訊)進行通信,並將兩個環境相互隔離。像這樣設定您的應用程式可以讓您驗證後端中可能是 require
模組的任何內容,而客戶端不會對其進行篡改。
下面,您將找到一個簡短的範例,介紹我所討論的內容以及它在您的應用程式中的外觀。如果您是新手,我可能建議使用secure-electron-template
< /a> (我是其作者)在構建電子應用程式時從一開始就融入了所有這些安全最佳實踐。
此頁面也有很好的資訊使用 preload.js 製作安全應用程式時所需的架構。
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>