电子:如何安全地将全局变量注入BrowserWindow / BrowserView?

J. *_*ers 5 javascript security reactjs electron

我想使用来在Electron中加载外部网页BrowserView。它具有与几乎相同的API BrowserWindow

const currentWindow = remote.getCurrentWindow();
const view = new remote.BrowserView({
  webPreferences: {
    // contextIsolation: true,
    partition: 'my-view-partition',
    enableRemoteModule: false,
    nodeIntegration: false,
    preload: `${__dirname}/preload.js`,
    sandbox: true,
  },
});
view.setAutoResize({ width: true, height: true });
view.webContents.loadURL('http://localhost:3000');
Run Code Online (Sandbox Code Playgroud)

在我的preload.js文件中,我只是将变量附加到全局对象。

process.once('loaded', () => {
  global.baz = 'qux';
});
Run Code Online (Sandbox Code Playgroud)

运行的应用程序localhost:3000是一个React应用程序,它引用如下所示的值:

const sharedString = global.baz || 'Not found';
Run Code Online (Sandbox Code Playgroud)

问题是contextIsolation: true创建时我必须注释掉设置BrowserView。这暴露了一个安全漏洞。

是否可以(一种方式-从Electron到网页)将变量注入到中BrowserView(或BrowserWindow),同时仍contextIsolation使Electron环境与加载的内容对全局环境所做的任何更改隔离开来?

更新: 一种可能的方法是拦截网络协议,但是我不确定

app.on('ready', () => {
  const { protocol } = session.fromPartition('my-partition')

  protocol.interceptBufferProtocol('https', (req, callback) => {
    if (req.uploadData) {
      // How to handle file uploads?
      callback()
      return
    }

    // This is electron.net, docs: https://electronjs.org/docs/api/net
    net
      .request(req)
      .on('response', (res) => {
        const chunks = []
        res.on('data', (chunk) => {
          chunks.push(Buffer.from(chunk))
        })
        res.on('end', () => {
          const blob = Buffer.concat(chunks)
          const type = res.headers['content-type'] || []
          if (type.includes('text/html') && blob.includes('<head>')) {
            // FIXME?
            const pos = blob.indexOf('<head>')
            // inject contains the Buffer with the injected HTML script
            callback(Buffer.concat([blob.slice(0, pos), inject, blob.slice(pos)]))
          } else {
            callback(blob)
          }
        })
      })
      .on('error', (err) => {
        console.error('error', err)
        callback()
      })
      .end()
  })
})
Run Code Online (Sandbox Code Playgroud)

dod*_*gez 8

在进行了一些挖掘之后,我发现了一些针对 Electron 的拉取请求,其中详细说明了您遇到的问题。第一个描述了一个与您所描述的问题非常相似的可重现示例。

预期行为

https://electronjs.org/docs/tutorial/security#3-enable-context-isolation-for-remote-content 预加载脚本应该能够使用 contextIsolation: true 将任何内容附加到窗口或文档。

实际行为

在 preload.js 中附加到窗口的任何东西都会在渲染器中消失。

似乎最后的评论解释了预期的行为不再有效

实际上直到最近才有可能,隔离世界的公关改变了行为。

第二个有一个用户有什么建议,他们发现他们的解决方案:

经过多天的研究和对 IPC 的摆弄,我得出的结论是,最好的方法是走协议路线。

我看了看文档的BrowserWindowBrowserView以及一个例子是节目,你的愿望,但这些永久居民认为这是不再可能(沿这条路线)的行为。

可能的解决方案

查看文档,webContents您从中获取的对象view.webContents具有函数executeJavaScript,因此您可以尝试以下操作来设置全局变量。

...
view.setAutoResize({ width: true, height: true });
view.webContents.loadURL('http://localhost:3000');
view.webContents.executeJavaScript("global.baz = 'qux';");
...
Run Code Online (Sandbox Code Playgroud)


qui*_*ley 5

其他答案已过时,使用contextBridge请务必使用sendToHost()而不是 send()

    // Preload (Isolated World)
    const { contextBridge, ipcRenderer } = require('electron')
    
    contextBridge.exposeInMainWorld(
      'electron',
      {
        doThing: () => ipcRenderer.sendToHost('do-a-thing')
      }
    )
    
    // Renderer (Main World)
    
    window.electron.doThing()
Run Code Online (Sandbox Code Playgroud)