安全性
如需關於如何正確揭露 Electron 漏洞的資訊,請參閱 SECURITY.md。
關於上游 Chromium 漏洞:Electron 與 Chromium 的交替發行版本保持同步。如需更多資訊,請參閱Electron 發行時程文件。
前言
身為網頁開發者,我們通常享有瀏覽器強大的安全防護網——我們編寫的程式碼相關風險相對較小。我們的網站在沙箱中被授予有限的權限,並且我們相信我們的使用者能享受到由大型工程團隊構建的瀏覽器,該團隊能夠快速應對新發現的安全威脅。
當使用 Electron 時,務必理解 Electron 不是網頁瀏覽器。它允許您使用熟悉的網頁技術構建功能豐富的桌面應用程式,但您的程式碼擁有更大的權力。JavaScript 可以存取檔案系統、使用者 Shell 和更多。這讓您可以構建高品質的原生應用程式,但固有的安全風險會隨著授予程式碼的額外權力而擴大。
記住這一點,請注意顯示來自不受信任來源的任意內容會帶來嚴重的安全風險,而 Electron 並非旨在處理這種情況。實際上,最受歡迎的 Electron 應用程式(Atom、Slack、Visual Studio Code 等)主要顯示本機內容(或受信任、安全的遠端內容,不含 Node 整合)——如果您的應用程式執行來自線上來源的程式碼,您有責任確保該程式碼不是惡意的。
一般準則
安全是每個人的責任
務必記住,您的 Electron 應用程式的安全性是框架基礎(Chromium、Node.js)、Electron 本身、所有 NPM 相依性以及您的程式碼的整體安全性的結果。因此,您有責任遵循一些重要的最佳實務。
-
讓您的應用程式保持在最新的 Electron 框架版本。 當發佈您的產品時,您也同時發佈了由 Electron、Chromium 共享函式庫和 Node.js 組成的套件。影響這些組件的漏洞可能會影響您的應用程式的安全性。透過將 Electron 更新到最新版本,您可以確保關鍵漏洞(例如 nodeIntegration 繞過)已被修補,並且無法在您的應用程式中被利用。如需更多資訊,請參閱「使用最新版本的 Electron」。
-
評估您的相依性。 雖然 NPM 提供了數十萬個可重複使用的套件,但您有責任選擇受信任的第三方函式庫。如果您使用受已知漏洞影響的過時函式庫或依賴維護不善的程式碼,您的應用程式安全性可能會受到危害。
-
採用安全的程式碼編寫實務。 您的應用程式的第一道防線是您自己的程式碼。常見的網頁漏洞,例如跨網站指令碼 (XSS),對 Electron 應用程式具有更高的安全影響,因此強烈建議採用安全的軟體開發最佳實務並執行安全性測試。
針對不受信任內容的隔離
每當您從不受信任的來源(例如,遠端伺服器)接收程式碼並在本機執行它時,就會存在安全問題。例如,考慮在預設 BrowserWindow
中顯示的遠端網站。如果攻擊者以某種方式設法更改了所述內容(透過直接攻擊來源,或透過位於您的應用程式和實際目的地之間),他們將能夠在使用者機器上執行原生程式碼。
在任何情況下,您都不應在啟用 Node.js 整合的情況下載入和執行遠端程式碼。相反地,僅使用本機檔案(與您的應用程式一起封裝)來執行 Node.js 程式碼。若要顯示遠端內容,請使用 <webview>
標籤或 WebContentsView
,並確保停用 nodeIntegration
並啟用 contextIsolation
。
安全性警告和建議會列印到開發人員主控台。它們僅在二進制檔案的名稱為 Electron 時才會顯示,表示開發人員目前正在查看主控台。
您可以透過在 process.env
或 window
物件上設定 ELECTRON_ENABLE_SECURITY_WARNINGS
或 ELECTRON_DISABLE_SECURITY_WARNINGS
來強制啟用或強制停用這些警告。
檢查清單:安全性建議
您至少應遵循以下步驟來提高應用程式的安全性。
- 僅載入安全內容
- 在所有顯示遠端內容的渲染器中停用 Node.js 整合
- 在所有渲染器中啟用上下文隔離
- 啟用處理程序沙箱
- 在所有載入遠端內容的 session 中使用
ses.setPermissionRequestHandler()
- 不要停用
webSecurity
- 定義
Content-Security-Policy
並使用限制性規則(即script-src 'self'
) - 不要啟用
allowRunningInsecureContent
- 不要啟用實驗性功能
- 不要使用
enableBlinkFeatures
<webview>
:不要使用allowpopups
<webview>
:驗證選項和參數- 停用或限制導航
- 停用或限制建立新視窗
- 請勿將
shell.openExternal
與不受信任的內容一起使用 - 使用最新版本的 Electron
- 驗證所有 IPC 訊息的
sender
- 避免使用
file://
協定,並優先使用自訂協定 - 檢查您可以變更哪些熔斷器
- 不要將 Electron API 暴露給不受信任的網頁內容
為了自動偵測錯誤配置和不安全的模式,可以使用 Electronegativity。關於使用 Electron 開發應用程式時潛在的弱點和實作錯誤的更多詳細資訊,請參閱這份開發人員和稽核人員指南。
1. 僅載入安全內容
任何未包含在您的應用程式中的資源都應使用安全協定(如 HTTPS
)載入。換句話說,不要使用不安全的協定(如 HTTP
)。同樣地,我們建議使用 WSS
而非 WS
、FTPS
而非 FTP
,依此類推。
為什麼?
HTTPS
有兩個主要優點:
- 它確保資料完整性,聲明資料在您的應用程式和主機之間傳輸時未被修改。
- 它加密您的使用者和目標主機之間的流量,使其更難以竊聽您的應用程式和主機之間發送的資訊。
如何做?
// Bad
browserWindow.loadURL('http://example.com')
// Good
browserWindow.loadURL('https://example.com')
<!-- Bad -->
<script crossorigin src="http://example.com/react.js"></script>
<link rel="stylesheet" href="http://example.com/style.css">
<!-- Good -->
<script crossorigin src="https://example.com/react.js"></script>
<link rel="stylesheet" href="https://example.com/style.css">
2. 不要為遠端內容啟用 Node.js 整合
自 Electron 5.0.0 以來,此建議為預設行為。
至關重要的是,您不要在任何載入遠端內容的渲染器(BrowserWindow
、WebContentsView
或 <webview>
)中啟用 Node.js 整合。目標是限制您授予遠端內容的權力,從而使攻擊者在獲得在您的網站上執行 JavaScript 的能力時,更難以傷害您的使用者。
在此之後,您可以為特定主機授予額外的權限。例如,如果您正在開啟一個指向 https://example.com/
的 BrowserWindow,您可以為該網站提供它需要的確切功能,但不要更多。
為什麼?
如果攻擊者可以跳出渲染器處理程序並在使用者電腦上執行程式碼,則跨網站指令碼 (XSS) 攻擊會更加危險。跨網站指令碼攻擊相當常見——雖然是個問題,但它們的力量通常僅限於破壞它們在其上執行的網站。停用 Node.js 整合有助於防止 XSS 升級為所謂的「遠端程式碼執行」(RCE) 攻擊。
如何做?
// Bad
const mainWindow = new BrowserWindow({
webPreferences: {
contextIsolation: false,
nodeIntegration: true,
nodeIntegrationInWorker: true
}
})
mainWindow.loadURL('https://example.com')
// Good
const mainWindow = new BrowserWindow({
webPreferences: {
preload: path.join(app.getAppPath(), 'preload.js')
}
})
mainWindow.loadURL('https://example.com')
<!-- Bad -->
<webview nodeIntegration src="page.html"></webview>
<!-- Good -->
<webview src="page.html"></webview>
當停用 Node.js 整合時,您仍然可以向您的網站公開使用 Node.js 模組或功能的 API。預先載入腳本繼續可以存取 require
和其他 Node.js 功能,允許開發人員透過 contextBridge API 向遠端載入的內容公開自訂 API。
3. 啟用上下文隔離
自 Electron 12.0.0 以來,上下文隔離是預設行為。
上下文隔離是 Electron 的一項功能,它允許開發人員在專用的 JavaScript 上下文中執行預先載入腳本和 Electron API 中的程式碼。實際上,這表示像 Array.prototype.push
或 JSON.parse
這樣的全域物件不能被在渲染器處理程序中執行的腳本修改。
Electron 使用與 Chromium 的 Content Scripts 相同的技術來啟用此行為。
即使使用 nodeIntegration: false
,為了真正強制執行強隔離並防止使用 Node 原語,也必須使用 contextIsolation
。
如需關於 contextIsolation
是什麼以及如何啟用的更多資訊,請參閱我們專門的上下文隔離文件。
4. 啟用處理程序沙箱
沙箱是 Chromium 的一項功能,它使用作業系統來顯著限制渲染器處理程序可以存取的內容。您應在所有渲染器中啟用沙箱。不建議在未沙箱的處理程序(包括主處理程序)中載入、讀取或處理任何不受信任的內容。
如需關於處理程序沙箱是什麼以及如何啟用的更多資訊,請參閱我們專門的處理程序沙箱文件。
5. 處理來自遠端內容的 session 權限請求
您可能在使用 Chrome 時看過權限請求:每當網站嘗試使用使用者必須手動批准的功能(例如通知)時,它們就會彈出。
API 基於 Chromium 權限 API,並實作相同類型的權限。
為什麼?
預設情況下,Electron 將自動批准所有權限請求,除非開發人員已手動配置自訂處理程序。雖然這是一個可靠的預設值,但具有安全意識的開發人員可能希望假設完全相反的情況。
如何做?
const { session } = require('electron')
const { URL } = require('url')
session
.fromPartition('some-partition')
.setPermissionRequestHandler((webContents, permission, callback) => {
const parsedUrl = new URL(webContents.getURL())
if (permission === 'notifications') {
// Approves the permissions request
callback(true)
}
// Verify URL
if (parsedUrl.protocol !== 'https:' || parsedUrl.host !== 'example.com') {
// Denies the permissions request
return callback(false)
}
})
6. 不要停用 webSecurity
此建議是 Electron 的預設值。
您可能已經猜到,停用渲染器處理程序(BrowserWindow
、WebContentsView
或 <webview>
)上的 webSecurity
屬性會停用關鍵的安全功能。
不要在生產應用程式中停用 webSecurity
。
為什麼?
停用 webSecurity
將停用同源策略,並將 allowRunningInsecureContent
屬性設定為 true
。換句話說,它允許執行來自不同網域的不安全程式碼。
如何做?
// Bad
const mainWindow = new BrowserWindow({
webPreferences: {
webSecurity: false
}
})
// Good
const mainWindow = new BrowserWindow()
<!-- Bad -->
<webview disablewebsecurity src="page.html"></webview>
<!-- Good -->
<webview src="page.html"></webview>
7. 定義內容安全策略
內容安全策略 (CSP) 是針對跨網站指令碼攻擊和資料注入攻擊的額外保護層。我們建議您為在 Electron 內載入的任何網站啟用它們。
為什麼?
CSP 允許提供內容的伺服器限制和控制 Electron 可以為給定網頁載入的資源。應該允許 https://example.com
從您定義的來源載入腳本,而不應允許來自 https://evil.attacker.com
的腳本執行。定義 CSP 是提高應用程式安全性的簡單方法。
如何做?
以下 CSP 將允許 Electron 執行來自目前網站和 apis.example.com
的腳本。
// Bad
Content-Security-Policy: '*'
// Good
Content-Security-Policy: script-src 'self' https://apis.example.com
CSP HTTP 標頭
Electron 尊重 Content-Security-Policy
HTTP 標頭,可以使用 Electron 的 webRequest.onHeadersReceived
處理程序來設定。
const { session } = require('electron')
session.defaultSession.webRequest.onHeadersReceived((details, callback) => {
callback({
responseHeaders: {
...details.responseHeaders,
'Content-Security-Policy': ['default-src \'none\'']
}
})
})
CSP meta 標籤
CSP 的首選傳遞機制是 HTTP 標頭。但是,當使用 file://
協定載入資源時,無法使用此方法。在某些情況下,直接在標記中使用 <meta>
標籤在頁面上設定策略可能很有用。
<meta http-equiv="Content-Security-Policy" content="default-src 'none'">
8. 不要啟用 allowRunningInsecureContent
此建議是 Electron 的預設值。
預設情況下,Electron 不允許透過 HTTPS
載入的網站從不安全來源 (HTTP
) 載入和執行腳本、CSS 或外掛程式。將屬性 allowRunningInsecureContent
設定為 true
會停用該保護。
透過 HTTPS
載入網站的初始 HTML 並嘗試透過 HTTP
載入後續資源也稱為「混合內容」。
為什麼?
透過 HTTPS
載入內容可確保載入資源的真實性和完整性,同時加密流量本身。有關更多詳細資訊,請參閱關於僅顯示安全內容的章節。
如何做?
// Bad
const mainWindow = new BrowserWindow({
webPreferences: {
allowRunningInsecureContent: true
}
})
// Good
const mainWindow = new BrowserWindow({})
9. 不要啟用實驗性功能
此建議是 Electron 的預設值。
Electron 的進階使用者可以使用 experimentalFeatures
屬性啟用實驗性 Chromium 功能。
為什麼?
實驗性功能顧名思義,是實驗性的,尚未為所有 Chromium 使用者啟用。此外,它們對整個 Electron 的影響可能尚未經過測試。
存在合理的用例,但除非您知道自己在做什麼,否則不應啟用此屬性。
如何做?
// Bad
const mainWindow = new BrowserWindow({
webPreferences: {
experimentalFeatures: true
}
})
// Good
const mainWindow = new BrowserWindow({})
10. 不要使用 enableBlinkFeatures
此建議是 Electron 的預設值。
Blink 是 Chromium 背後的渲染引擎的名稱。與 experimentalFeatures
一樣,enableBlinkFeatures
屬性允許開發人員啟用預設情況下已停用的功能。
為什麼?
一般來說,如果某項功能預設情況下未啟用,則可能存在充分的理由。啟用特定功能的合理用例確實存在。作為開發人員,您應該確切地知道為什麼需要啟用某項功能、其後果是什麼以及它如何影響應用程式的安全性。在任何情況下,您都不應推測性地啟用功能。
如何做?
// Bad
const mainWindow = new BrowserWindow({
webPreferences: {
enableBlinkFeatures: 'ExecCommandInJavaScript'
}
})
// Good
const mainWindow = new BrowserWindow()
11. 不要對 WebView 使用 allowpopups
此建議是 Electron 的預設值。
如果您正在使用 <webview>
,您可能需要載入到 <webview>
標籤中的頁面和腳本來開啟新視窗。allowpopups
屬性使它們能夠使用 window.open()
方法建立新的 BrowserWindow
。否則,<webview>
標籤不允許建立新視窗。
為什麼?
如果您不需要彈出視窗,最好預設情況下不允許建立新的 BrowserWindow
。這遵循最小必要存取的原則:除非您知道網站需要該功能,否則不要讓網站建立新的彈出視窗。
如何做?
<!-- Bad -->
<webview allowpopups src="page.html"></webview>
<!-- Good -->
<webview src="page.html"></webview>
12. 在建立之前驗證 WebView 選項
在未啟用 Node.js 整合的渲染器處理程序中建立的 WebView 將無法自行啟用整合。但是,WebView 將始終使用其自己的 webPreferences
建立獨立的渲染器處理程序。
最好從主處理程序控制新 <webview>
標籤的建立,並驗證它們的 webPreferences 是否未停用安全功能。
為什麼?
由於 <webview>
存在於 DOM 中,因此即使 Node.js 整合以其他方式停用,它們也可以由在您的網站上執行的腳本建立。
Electron 使開發人員能夠停用控制渲染器處理程序的各種安全功能。在大多數情況下,開發人員不需要停用任何這些功能——因此,您不應允許為新建立的 <webview>
標籤設定不同的配置。
如何做?
在附加 <webview>
標籤之前,Electron 將在託管 webContents
上觸發 will-attach-webview
事件。使用此事件來防止建立具有可能不安全選項的 webViews
。
app.on('web-contents-created', (event, contents) => {
contents.on('will-attach-webview', (event, webPreferences, params) => {
// Strip away preload scripts if unused or verify their location is legitimate
delete webPreferences.preload
// Disable Node.js integration
webPreferences.nodeIntegration = false
// Verify URL being loaded
if (!params.src.startsWith('https://example.com/')) {
event.preventDefault()
}
})
})
再次強調,此清單僅最大限度地降低風險,但並未消除風險。如果您的目標是顯示網站,則瀏覽器將是更安全的選擇。
13. 停用或限制導航
如果您的應用程式不需要導航,或者只需要導航到已知的頁面,那麼最好將導航完全限制在已知的範圍內,不允許任何其他類型的導航。
為什麼?
導航是常見的攻擊媒介。如果攻擊者可以說服您的應用程式導航離開其當前頁面,他們可能會強制您的應用程式開啟網際網路上的網站。即使您的 webContents
配置為更安全(例如停用 nodeIntegration
或啟用 contextIsolation
),讓您的應用程式開啟隨機網站也會讓利用您的應用程式變得容易得多。
常見的攻擊模式是攻擊者說服您的應用程式的使用者以某種方式與應用程式互動,使其導航到攻擊者的頁面之一。這通常透過連結、外掛程式或其他使用者產生的內容來完成。
如何做?
如果您的應用程式不需要導航,您可以在 will-navigate
處理程序中呼叫 event.preventDefault()
。如果您知道您的應用程式可能會導航到哪些頁面,請檢查事件處理程序中的 URL,並且僅在它與您期望的 URL 匹配時才允許導航發生。
我們建議您使用 Node 的 parser 來解析 URL。簡單的字串比較有時可能會被愚弄——startsWith('https://example.com')
測試會讓 https://example.com.attacker.com
通過。
const { URL } = require('url')
const { app } = require('electron')
app.on('web-contents-created', (event, contents) => {
contents.on('will-navigate', (event, navigationUrl) => {
const parsedUrl = new URL(navigationUrl)
if (parsedUrl.origin !== 'https://example.com') {
event.preventDefault()
}
})
})
14. 停用或限制建立新視窗
如果您有一組已知的視窗,最好限制在您的應用程式中建立額外視窗。
為什麼?
與導航非常相似,建立新的 webContents
是一個常見的攻擊媒介。攻擊者試圖說服您的應用程式建立新的視窗、框架或其他渲染器處理程序,這些處理程序具有比以前更多的權限;或者開啟他們以前無法開啟的頁面。
如果您不需要建立除了您知道您需要建立的視窗之外的視窗,停用建立視窗會為您帶來一點額外的安全性,而無需付出任何代價。對於開啟一個 BrowserWindow
並且不需要在運行時開啟任意數量的額外視窗的應用程式來說,通常是這種情況。
如何做?
webContents
將委派給其視窗開啟處理程序,然後再建立新視窗。處理程序將接收視窗請求開啟的 url
和用於建立視窗的選項等參數。我們建議您註冊一個處理程序來監控視窗的建立,並拒絕任何意外的視窗建立。
const { app, shell } = require('electron')
app.on('web-contents-created', (event, contents) => {
contents.setWindowOpenHandler(({ url }) => {
// In this example, we'll ask the operating system
// to open this event's url in the default browser.
//
// See the following item for considerations regarding what
// URLs should be allowed through to shell.openExternal.
if (isSafeForExternalOpen(url)) {
setImmediate(() => {
shell.openExternal(url)
})
}
return { action: 'deny' }
})
})
15. 請勿將 shell.openExternal
與不受信任的內容一起使用
shell 模組的 openExternal
API 允許使用桌面的原生公用程式開啟給定的協定 URI。例如,在 macOS 上,此函數類似於 open
終端命令公用程式,並將根據 URI 和檔案類型關聯開啟特定的應用程式。
為什麼?
不當使用 openExternal
可能會被利用來危害使用者的主機。當 openExternal 與不受信任的內容一起使用時,它可以被利用來執行任意命令。
如何做?
// Bad
const { shell } = require('electron')
shell.openExternal(USER_CONTROLLED_DATA_HERE)
// Good
const { shell } = require('electron')
shell.openExternal('https://example.com/index.html')
16. 使用最新版本的 Electron
您應該努力始終使用最新可用的 Electron 版本。每當發佈新的主要版本時,您都應嘗試盡快更新您的應用程式。
為什麼?
使用舊版 Electron、Chromium 和 Node.js 建構的應用程式比使用這些組件的較新版本的應用程式更容易成為目標。一般來說,舊版 Chromium 和 Node.js 的安全問題和漏洞利用更廣為人知。
Chromium 和 Node.js 都是由數千名才華橫溢的開發人員構建的令人印象深刻的工程壯舉。鑑於它們的普及程度,它們的安全性經過同樣熟練的安全研究人員的仔細測試和分析。許多研究人員負責任地揭露漏洞,這通常意味著研究人員會給 Chromium 和 Node.js 一些時間來修復問題,然後再發佈它們。如果您的應用程式運行的是最新版本的 Electron(以及 Chromium 和 Node.js),潛在的安全問題就不會那麼廣為人知,您的應用程式將會更安全。
如何做?
一次遷移您的應用程式一個主要版本,同時參考 Electron 的重大變更文件,以查看是否需要更新任何程式碼。
17. 驗證所有 IPC 訊息的 sender
您應始終驗證傳入的 IPC 訊息 sender
屬性,以確保您不會對不受信任的渲染器執行操作或發送資訊。
為什麼?
所有 Web Frame 在理論上都可以向主處理程序發送 IPC 訊息,包括在某些情況下的 iframe 和子視窗。如果您有一個 IPC 訊息透過 event.reply
將使用者資料返回給發送者,或執行渲染器無法原生執行的特權操作,則應確保您沒有監聽第三方 Web Frame。
您應該預設驗證所有 IPC 訊息的 sender
。
如何做?
// Bad
ipcMain.handle('get-secrets', () => {
return getSecrets()
})
// Good
ipcMain.handle('get-secrets', (e) => {
if (!validateSender(e.senderFrame)) return null
return getSecrets()
})
function validateSender (frame) {
// Value the host of the URL using an actual URL parser and an allowlist
if ((new URL(frame.url)).host === 'electronjs.org') return true
return false
}
18. 避免使用 file://
協定,並優先使用自訂協定
您應該從自訂協定而不是 file://
協定提供本機頁面。
為什麼?
file://
協定在 Electron 中比在網頁瀏覽器中獲得更多權限,即使在瀏覽器中,它的處理方式也與 http/https URL 不同。使用自訂協定可讓您更符合經典網頁 URL 行為,同時保留更多關於何時以及可以載入什麼的控制權。
在 file://
上運行的頁面可以單方面存取您機器上的每個檔案,這表示 XSS 問題可用於從使用者機器載入任意檔案。使用自訂協定可以防止此類問題,因為您可以將協定限制為僅提供一組特定的檔案。
如何做?
遵循 protocol.handle
範例,了解如何從自訂協定提供檔案/內容。
19. 檢查您可以變更哪些熔斷器
Electron 附帶了許多可能有用的選項,但大多數應用程式可能不需要。為了避免必須建構您自己的 Electron 版本,可以使用熔斷器來關閉或開啟這些選項。
為什麼?
某些熔斷器(例如 runAsNode
和 nodeCliInspect
)允許應用程式在使用特定環境變數或 CLI 參數從命令列運行時表現不同。這些可用於透過您的應用程式在裝置上執行命令。
這可以讓外部腳本運行它們可能不被允許的命令,但您的應用程式可能具有這些權限。
如何做?
我們製作了一個模組 @electron/fuses
,以簡化這些熔斷器的切換。查看該模組的 README 以獲得關於用法和潛在錯誤案例的更多詳細資訊,並參閱我們文件中的如何切換熔斷器?。
20. 不要將 Electron API 暴露給不受信任的網頁內容
您不應直接將 Electron 的 API(尤其是 IPC)暴露給預先載入腳本中不受信任的網頁內容。
為什麼?
暴露原始 API,例如 ipcRenderer.on
是危險的,因為它讓渲染器進程可以直接存取整個 IPC 事件系統,使它們能夠監聽任何 IPC 事件,而不僅僅是那些預期給它們的事件。
為了避免這種暴露,我們也不能直接傳遞回呼:IPC 事件回呼的第一個參數是一個 IpcRendererEvent
物件,它包含諸如 sender
之類的屬性,這些屬性提供了對底層 ipcRenderer
實例的存取權限。即使您僅監聽特定事件,直接傳遞回呼也意味著渲染器可以存取這個事件物件。
簡而言之,我們希望不受信任的網頁內容僅能存取必要的資訊和 API。
如何?
// Bad
contextBridge.exposeInMainWorld('electronAPI', {
on: ipcRenderer.on
})
// Also bad
contextBridge.exposeInMainWorld('electronAPI', {
onUpdateCounter: (callback) => ipcRenderer.on('update-counter', callback)
})
// Good
contextBridge.exposeInMainWorld('electronAPI', {
onUpdateCounter: (callback) => ipcRenderer.on('update-counter', (_event, value) => callback(value))
})
有關 contextIsolation
是什麼以及如何使用它來保護您的應用程式的更多資訊,請參閱Context Isolation 文件。