Problem
For my private use, I’m making an Electron app. My issue is that when I use node functions in my HTML website, I get the following error:
Is it possible to use Node functionality across all of my HTML pages? If possible, please show me or send me a link to an example of how to do this. The variables I’m attempting to use in my HTML page are as follows:
var app = require('electron').remote;
var dialog = app.dialog;
var fs = require('fs');
These are the values I’m using in all of my Electron HTML windows.
Asked by Mari Selvan
Solution #1
The default value for nodeIntegration has changed from true to false in version 5. When creating the Browser Window, you can choose to enable it:
app.on('ready', () => {
mainWindow = new BrowserWindow({
webPreferences: {
nodeIntegration: true,
contextIsolation: false,
}
});
});
Answered by Sathiraumesh
Solution #2
I’m hoping my answer gets some attention because the vast majority of the answers here leave your electron app with serious security flaws. In reality, if you want to use require() in your electron programs, you should do exactly what this response says. (In v7, there’s a new electron API that makes things a little cleaner.)
I wrote a thorough explanation/solution in github for how to require() something using the most recent electron apis, but I’ll explain simply here why you should use a preload script, contextBridge, and ipc.
Electron apps are amazing because they let us to utilise node, but this power comes with a price. If we aren’t careful, we can provide someone access to node via our app, and a bad actor can use node to corrupt your machine or remove your operating system files (among other things, I imagine).
This issue appears when you (do any of the following):
All of these issues allow your renderer process to access node without interruption. You can consider everything gone if your renderer process is ever hijacked.
The answer is to provide our electron main process access to require instead of giving the renderer direct access to node (i.e. require()), and whenever our renderer process needs to use require, marshal a request to the main process.
In the newest versions of Electron (7+), this is accomplished by setting up ipcRenderer bindings on the renderer side and ipcMain bindings on the main side. We put up listener methods in the ipcMain bindings that use modules we need (). This is great because our primary process can ask for whatever it wants.
We use the contextBridge to deliver the ipcRenderer bindings to our app code (to use), and then when our app needs to utilize the required modules in main, it sends a message over IPC (inter-process-communication), the main process performs some code, and we send back a message with our result.
Here’s a rough outline of what you want to do.
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>
I’m the creator of secure-electron-template, a secure electron app template. This is a topic that I am passionate about, and I have been working on it for a few weeks (at this point in time).
Answered by reZach
Solution #3
Keep nodeIntegration: false for security reasons, and use a preload script to expose only what you need from the Node/Electron API to the renderer process (view) via the window variable. The following is taken from the Electron docs:
main.js
const mainWindow = new BrowserWindow({
webPreferences: {
preload: path.join(app.getAppPath(), 'preload.js')
}
})
preload.js
const { remote } = require('electron');
let currWindow = remote.BrowserWindow.getFocusedWindow();
window.closeCurrentWindow = function(){
currWindow.close();
}
renderer.js
let closebtn = document.getElementById('closebtn');
closebtn.addEventListener('click', (e) => {
e.preventDefault();
window.closeCurrentWindow();
});
Answered by Rafael Croffi
Solution #4
To begin with, the @Sathiraumesh solution poses a significant security risk to your electron application. Consider that your software adds some extra functions to messenger.com, such as changing or blinking the toolbar symbol when you have an unread message. So, in your main.js file, create a new BrowserWindow as follows (note how I misspelled messenger.com on purpose):
app.on('ready', () => {
const mainWindow = new BrowserWindow({
webPreferences: {
nodeIntegration: true
}
});
mainWindow.loadURL(`https://messengre.com`);
});
What if messengre.com is a malicious website looking to infect your computer? If nodeIntegration: true is set, this site has access to your local file system and can run the following code:
require('child_process').exec('rm -r ~/');
Your home directory has vanished.
Solution Instead of exposing everything, only show what you need. This is accomplished by using require statements to preload javascript code.
// main.js
app.on('ready', () => {
const mainWindow = new BrowserWindow({
webPreferences: {
preload: `${__dirname}/preload.js`
}
});
mainWindow.loadURL(`https://messengre.com`);
});
// preload.js
window.ipcRenderer = require('electron').ipcRenderer;
// index.html
<script>
window.ipcRenderer.send('channel', data);
</script>
Messagegre.com can no longer destroy your complete file system.
Answered by Legolando
Solution #5
Electron’s security appears to have grown in this manner (source).
The default value for nodeIntegration in Electron 1 is true.
Renderer has complete access to the Node API, posing significant security vulnerabilities if it loads outside code.
The default value for nodeIntegration in Electron 5 is false.
When set to false, a preload script is used to expose Renderer to specified API. (Regardless of the value of nodeIntegration, the preload script always gets access to Node APIs.)
//preload.js
window.api = {
deleteFile: f => require('fs').unlink(f)
}
Context of Electron 5 Isolation is set to true by default (actually still defaults to false in Electron 11)
As a result, the preload script runs in a distinct environment. You can’t do window any longer. api equals…. You must now complete the following tasks:
//preload.js
const { contextBridge } = require('electron')
contextBridge.exposeInMainWorld('api', {
deleteFile: f => require('fs').unlink(f)
})
In sandboxed renderers, require()ing node builtins in Electron 6 no longer loads the remote version.
You must do the following if Renderer’s sandbox is set to true:
//preload.js
const { contextBridge, remote } = require('electron')
contextBridge.exposeInMainWorld('api', {
deleteFile: f => remote.require('fs').unlink(f)
})
enableRemoteModule in Electron 10 is set to false by default (remote module deprecated in Electron 12)
When you need to access Node APIs from a sandboxed Renderer (like in the example above), or when you need to access Electron APIs that are only exposed to the Main process, the remote module is used (such as dialog, menu). You’ll need to write explicit IPC handlers if you don’t have remote.
//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))
The nodeIntegration flag is deprecated in Electron 10. (removed in Electron 12)
Set nodeIntegration: false, context to false at all times. True isolation; enable RemoteModule is set to false.
Set sandbox: true for maximum security. To do everything, your preload script will have to use IPC to call the Main process.
Your preload script can directly access Node API if sandbox is false, like in require(‘fs’). readFile. As long as you don’t do this, you’re safe:
//bad
contextBridge.exposeInMainWorld('api', {
readFile: require('fs').readFile
})
Answered by Sarsaparilla
Post is based on https://stackoverflow.com/questions/44391448/electron-require-is-not-defined