Electron Security |
Electron combines Node.js and browser environments, making desktop applications highly powerful, but also introducing certain security risks.
Proper security strategies can reduce the risks of XSS, Remote Code Execution (RCE), and data leakage.
Security Checklist and Common Vulnerabilities
Security Checklist
- Enable context isolation (
contextIsolation) - Use preload scripts (
preload.js) to expose secure APIs - Avoid directly using Node.js modules in the renderer process
- Enable sandbox mode (
sandbox: true) - Configure Content Security Policy (CSP)
- Use IPC securely, verifying source and input
- Limit application access to system permissions, following the principle of least privilege
- Avoid loading untrusted remote content
Common Security Vulnerabilities
- Remote content injection: Loading unknown URLs or HTML files may execute malicious JS
- Insecure IPC calls: The renderer process can invoke arbitrary main process methods
- Arbitrary Node.js execution: Enabling
nodeIntegrationin the renderer process can be exploited to execute system commands - XSS attacks: User input is rendered directly onto the page
- Vulnerable dependencies: Using outdated third-party libraries
Context Isolation
Understanding contextIsolation
Context isolation allows the renderer process and main process to run in separate JavaScript environments.
The webpage's JS in the renderer process cannot directly access Node.js APIs; instead, it accesses them through a secure preload script.
const mainWindow = new BrowserWindow({ webPreferences: { contextIsolation: true, // Enable context isolation preload: path.join(__dirname, 'preload.js'), nodeIntegration: false // Disable Node.js usage in the renderer process }})
Why Context Isolation Is Needed
- Prevents loaded webpages or third-party scripts from directly accessing Node.js
- Avoids attackers gaining system privileges via XSS
- Keeps the renderer process handling only UI, while the main process handles system operations
Migrating to Isolated Context
- Use
contextBridge.exposeInMainWorldto expose secure APIs - Place all system operations in the main process and invoke them via IPC
Example:
// preload.js
const { contextBridge, ipcRenderer } = require('electron')
contextBridge.exposeInMainWorld('api', {
getData: () => ipcRenderer.invoke('get-data')
})
The renderer process only calls window.api.getData() and cannot directly access the system.
Sandbox Mode
Enabling Sandbox
Sandbox mode makes the renderer process run like a regular browser webpage, completely isolating the Node.js environment.
const mainWindow = new BrowserWindow({ webPreferences: { sandbox: true, contextIsolation: true, preload: path.join(__dirname, 'preload.js') }})
Sandbox Limitations and Mitigations
- Limitation: The renderer process cannot directly access Node.js modules
- Mitigation: Use IPC to invoke secure methods provided by the main process
- Advantage: Enhanced security, preventing malicious scripts from compromising the system
Content Security Policy (CSP)
CSP prevents XSS and data injection attacks by restricting the sources from which a webpage can load resources.
CSP Configuration Example
<meta http-equiv="Content-Security-Policy" content=" default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'; img-src 'self' data:; connect-src 'self' https://api.example.com;">
Explanation:
default-src 'self': By default, only same-origin resources are allowedscript-src 'self': Only local scripts are allowedstyle-src 'self' 'unsafe-inline': Inline styles are allowedconnect-src: Restricts AJAX/WebSocket request domains
Preventing XSS Attacks
- Never use
innerHTMLdirectly in the renderer process to display user input - Escape user input or use framework template rendering
Secure IPC Communication
Verifying Message Source
The main process should distinguish the renderer process source to prevent arbitrary execution.
Example:
ipcMain.handle('do-action', (event, data) => {
if (event.senderFrame.url.startsWith('file://')) {
// Secure source, perform operation
} else {
console.warn('Insecure IPC request blocked')
}
})
Input Validation
- All data passed via IPC should be validated for type and range
- Avoid injecting malicious commands or file paths
function validateInput(data) {
if (typeof data !== 'string' || data.length > 100) throw new Error('Invalid input')
return data
}
Principle of Least Privilege
- Expose only necessary functionality to the renderer process
- Do not expose all Node.js APIs or system permissions
- Use preload scripts to control the scope of interface access
Summary
The core of Electron security best practices is isolation + validation + restriction:
- Isolation: Enable context isolation and sandbox mode
- Validation: CSP, XSS prevention, IPC input validation
- Restriction: Minimize renderer process permissions and provide only necessary APIs
Following these principles significantly reduces the risk of desktop applications being attacked or suffering data breaches.
YouTip