如果contextIsolation = true,则可以使用ipcRenderer吗?

时间:2019-03-14 13:56:11

标签: security electron

这是我的设置:

步骤1。使用以下代码创建preload.js文件:

window.ipcRenderer = require('electron').ipcRenderer;

第2步。通过webPreferences将文件预加载到main.js中:

  mainWindow = new BrowserWindow({
    width: 800, 
    height: 600,
    webPreferences: {
      nodeIntegration: false,
      preload: __dirname + '/preload.js'
    }
  });

第3步。在渲染器中:

console.log(window.ipcRenderer); // Works!

现在,按照Electron的安全性指南,我希望转向contextIsolation=truehttps://electronjs.org/docs/tutorial/security#3-enable-context-isolation-for-remote-content

步骤2之二。

  mainWindow = new BrowserWindow({
    width: 800, 
    height: 600,
    webPreferences: {
      contextIsolation: true,
      nodeIntegration: false,
      preload: __dirname + '/preload.js'
    }
  });

步骤3之二。在渲染器中:

console.log(window.ipcRenderer); // undefined

问题:contextIsolation=true时可以使用ipcRenderer吗?

3 个答案:

答案 0 :(得分:4)

新答案

您可以遵循设置outlined heresecure-electron-template中正在使用此设置,基本上,您可以执行以下操作:

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>

原始

您还可以在contextIsolation设置为true的渲染器进程中使用ipcRenderer。 contextBridge是您要使用的,尽管有一个current bug阻止了您在渲染器过程中调用ipcRenderer.on。您所能做的就是从渲染器进程发送到主进程。

此代码摘自secure-electron-template电子安全模板,该模板是为安全性而构建的。 (我是作者)

preload.js

const { contextBridge, ipcRenderer } = require("electron");

contextBridge.exposeInMainWorld(
    "electron",
    {
        ipcRenderer: ipcRenderer
    }
);

main.js

let win;

async function createWindow() {

  // Create the browser window.
  win = new BrowserWindow({
    width: 800,
    height: 600,
    webPreferences: {
      nodeIntegration: false,
      nodeIntegrationInWorker: false,
      nodeIntegrationInSubFrames: false,
      contextIsolation: true,
      enableRemoteModule: false,
      preload: path.join(__dirname, "preload.js")
    }
  });
}

一些renderer.js文件

window.electron.ipcRenderer

答案 1 :(得分:0)

description of context isolation的中间注意此句子。很容易错过。

  

Electron API仅在preload脚本中可用,而在加载的页面中不可用。

看起来答案是否定的。

答案 2 :(得分:0)

请检查this。这个对我有用。我正在使用CRA和Electron。

preload.js


    const { contextBridge, ipcRenderer } = require('electron');
    const MESSAGE_TYPES = ipcRenderer.sendSync('GET_MESSAGE_TYPES');
    
    require = null;
    
    class SafeIpcRenderer { ... }
    
    const registerMessages = () => {
      const safeIpcRenderer = new SafeIpcRenderer(Object.values(MESSAGE_TYPES));
    
      contextBridge.exposeInMainWorld('ELECTRON', {
        sendMessage: safeIpcRenderer.send,
        onReceiveMessage: safeIpcRenderer.on,
        MESSAGE_TYPES,
      });
    };
    
    registerMessages();

main.js


    const registerPreloadImports = require('./src/client/preloadUtils');
    
    // Required if sandbox flag is set to true. Non-electron modules cannot be directly imported in preload script.
    // For more info please check https://www.electronjs.org/docs/api/sandbox-option
    registerPreloadImports();
    
    let mainWindow = new BrowserWindow({
      // Web preferences for mainWindow
      webPreferences: {
        preload: path.join(__dirname, 'src/client/preload.js'),
        contextIsolation: true, // TODO: Remove it once it's enabled by default (from Electron v12)
        disableBlinkFeatures: 'Auxclick',
        sandbox: true,
        // https://www.electronjs.org/docs/api/sandbox-option#status
        enableRemoteModule: false,
      },
    });

preloadUtils.js


    const { ipcMain } = require('electron');
    const MESSAGE_TYPES = require('../utils/messageTypes');
    
    const registerPreloadImports = () => {
      ipcMain.on(MESSAGE_TYPES.GET_MESSAGE_TYPES, (event, message) => {
        event.returnValue = MESSAGE_TYPES;
      });
    };
    
    module.exports = registerPreloadImports;

messageTypes.js


    module.exports = {
      DNS_ONLINE_STATUS: 'dns-online-status',
      APP_ONLINE_STATUS: 'online-status',
      ONLINE_MODEL_SYNC: 'online-model-sync',
      APP_ONLINE: 'app-online',
      INITIAL_DATA_SYNC: 'INITIAL_DATA_SYNC',
      GET_MESSAGE_TYPES: 'GET_MESSAGE_TYPES',
    };

actions.js (渲染器)


    const { MESSAGE_TYPES, sendMessage } = window.ELECTRON || {};
    
    if (!MESSAGE_TYPES) return;
    
    const actions = {
      [MESSAGE_TYPES.INITIAL_DATA_SYNC]: (event, initialSync) => {
        console.log(MESSAGE_TYPES.INITIAL_DATA_SYNC, initialSync);
      },
    
      [MESSAGE_TYPES.ONLINE_MODEL_SYNC]: (event, message) => {
        console.log(MESSAGE_TYPES.ONLINE_MODEL_SYNC, message);
      },
    
      [MESSAGE_TYPES.APP_ONLINE]: (event, isOnline) => {
        console.log(MESSAGE_TYPES.APP_ONLINE, isOnline);
      },
    };
    
    const registerActions = () => {
      const { onReceiveMessage } = window.ELECTRON;
    
      Object.keys(actions).forEach((messageType) => {
        onReceiveMessage(messageType, actions[messageType]);
      });
    };
    
    registerActions();

package.json


    {
      "dependencies": {
        "cross-env": "7.0.2",
        "deepmerge": "4.2.2",
        "electron-is-dev": "1.2.0",
        "electron-log": "4.2.2",
        "electron-updater": "4.3.1",
        "sequelize-cli": "6.2.0",
        "sequelize": "6.3.3",
        "sqlite3": "5.0.0",
        "umzug": "2.3.0",
        "uuid": "8.2.0"
      },
      "devDependencies": {
        "concurrently": "5.2.0",
        "electron": "9.1.0",
        "electron-builder": "22.7.0",
        "spectron": "11.1.0",
        "wait-on": "5.1.0",
        "xvfb-maybe": "0.2.1"
      }
    }