Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Electron IPC and nodeIntegration

So, I've followed a number of guides to set up Webpack, Electron, and React to make a desktop application. After finishing the setup, I got to work, and learned that I needed to require an IPC mechanism from the main and renderer in order to communicate.

import {ipcRenderer} from "electron"; Adding this to my renderer.js file causes the error Uncaught ReferenceError: require is not defined.

After taking my problem to some colleagues, it was suggested that in my main.js file I should change

webPreferences: {     nodeIntegration: false, } 

to

webPreferences: {     nodeIntegration: true, } 

Everywhere I've read on google has said very clearly that if safety is something you care about, this is not something you should do. However, every resource I've been able to come across for electron ipc has used the ipcRenderer.

Now, does every example on the internet have huge security flaws, or am I missing some key part here?

My questions are as follows.

  1. Is it possible to use ipcRenderer without enabling nodeIntegration?
  2. If it is, how do I do it, and why would so many resources exclude this information?
  3. If it is not, what do I use?

If I'm asking the wrong question, or I missed something, or there are any other clear problems with the way I've asked this question please let me know, otherwise thanks in advance.

like image 692
Antflga Avatar asked Sep 08 '18 15:09

Antflga


People also ask

What is Electron Nodeintegration?

Electron node integration refers to the ability of accessing Node. js resources from within the “renderer” thread (the UI). It is enabled by default in Quasar CLI, although Electron is encouraging developers to turn it off as a security precaution.

Does Electron require node JS?

To use Electron, you need to install Node. js. We recommend that you use the latest LTS version available.

What is main process and renderer process in Electron?

Electron uses multi-process architecture to manage the application state and user interface. The main process controls the state of the application while the renderer process controls the user interface. Electron uses GPU process to perform graphically intensive tasks but it's optional.


1 Answers

  1. Is it possible to use ipcRenderer without enabling nodeIntegration?

It is possible, but fiddly. It can be done by using a preload script.

  1. If it is, how do I do it, and why would so many resources exclude this information?

It is possible, using the preload script as indicated below. However, this is not considered secure. Most of the existing documentation does not show best security practices.

A more secure example is given afterwards.

// preload.js const electron = require('electron');  process.once('loaded', () => {   global.ipcRenderer = electron.ipcRenderer; }); 
// main.js const {app, BrowserWindow} = require('electron');  app.on('ready', () => {   // Create the browser window.   win = new BrowserWindow({       backgroundColor: '#fff', // always set a bg color to enable font antialiasing!       webPreferences: {         preload: path.join(__dirname, './preload.js'),         nodeIntegration: false,         enableRemoteModule: false,         // contextIsolation: true,         // nativeWindowOpen: true,         // sandbox: true,       }   });   win.loadURL(`file://${path.join(__dirname, 'index.html')}`); 

NOTE That the path to the preload script must be absolute and this can also get complicated when using webpack/babel, as the output file may be a different path.

  1. If it is not, what do I use?

Edit As @Yannic pointed out, there is now another option supported by Electron, called contextBridge. This new option may solve the problem more simply. For info on contextBridge, check the electron docs: https://www.electronjs.org/docs/tutorial/context-isolation

However, even with contextBridge you should not be try to expose entire electron APIs, just a limited API you have designed for your app

As mentioned, although it is possible to use ipcRenderer as shown above, the current electron security recommendations recommend also enabling contextIsolation. This will make the above approach unusable as you can no longer add data to the global scope.

The most secure recommendation, AFAIK is to use addEventListener and postMessage instead, and use the preload script as a bridge between the renderer and the main scripts.

// preload.js const { ipcRenderer } = require('electron');  process.once('loaded', () => {   window.addEventListener('message', event => {     // do something with custom event     const message = event.data;      if (message.myTypeField === 'my-custom-message') {       ipcRenderer.send('custom-message', message);     }   }); }); 
// main.js const {app, ipcMain, BrowserWindow} = require('electron');  app.on('ready', () => {   ipcMain.on('custom-message', (event, message) => {     console.log('got an IPC message', e, message);   });    // Create the browser window.   win = new BrowserWindow({       backgroundColor: '#fff', // always set a bg color to enable font antialiasing!       webPreferences: {         preload: path.join(__dirname, './preload.js'),         nodeIntegration: false,         enableRemoteModule: false,         contextIsolation: true,         sandbox: true,         // nativeWindowOpen: true,       }   });   win.loadURL(`file://${path.join(__dirname, 'index.html')}`); 
// renderer.js window.postMessage({   myTypeField: 'my-custom-message',   someData: 123, }); 
like image 199
Luke H Avatar answered Sep 22 '22 09:09

Luke H