【问题标题】:With contextIsolation = true, is it possible to use ipcRenderer?如果 contextIsolation = true,是否可以使用 ipcRenderer?
【发布时间】:2019-08-05 10:57:24
【问题描述】:

这是我的设置:

步骤 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=true:https://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 吗?

【问题讨论】:

  • 如果您仍在寻找答案,我已经更新了我的答案。

标签: security electron


【解决方案1】:

新答案

您可以按照设置outlined heresecure-electron-template 正在使用此设置基本上,您可以这样做:

ma​​in.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 一个为 Electron 构建的模板,考虑到了安全性。 (我是作者)

preload.js

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

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

ma​​in.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

【讨论】:

  • 这仍然不安全。即使使用 contextBridge,将整个 ipcRenderer 作为方法公开给渲染器也可能导致滥用:electronjs.org/docs/tutorial/…
  • @user1679669 我们在 ipcRenderer 上公开了允许操作的 子集,这与您发布的链接不同。请参阅新答案部分,了解您可以做什么的更好示例。
【解决方案2】:

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

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

看起来答案是否定的。

【讨论】:

  • 你知道在这种情况下主进程和渲染器进程如何通信吗?
  • @amaurymartiny 根据我的测试,Node 在渲染器中被完全禁用(即使没有webview),我不能要求ipcRenderer,所以它看起来不像渲染器可以和主对话。话虽如此,这种行为有点奇怪,因为我认为它只会影响 webview/preload,这也是我发布 this 问题的原因。
  • @amaurymartiny This 可以提供帮助。看起来 Electron API 在 preload 和 main 中可用。因此,您仍然应该能够将消息从 preload 发送到 main,而不是从 webview/preload 渲染器发送到“包装”它的其他渲染器
【解决方案3】:

请查看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();

ma​​in.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"
      }
    }

【讨论】:

    猜你喜欢
    • 2021-08-14
    • 2015-02-24
    • 2018-10-08
    • 1970-01-01
    • 2015-06-07
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-05-12
    相关资源
    最近更新 更多