Skip to content

Add context isolation option to windows and webview tags#8348

Merged
kevinsawicki merged 44 commits intomasterfrom
isolated-world
Jan 17, 2017
Merged

Add context isolation option to windows and webview tags#8348
kevinsawicki merged 44 commits intomasterfrom
isolated-world

Conversation

@kevinsawicki
Copy link
Copy Markdown
Contributor

@kevinsawicki kevinsawicki commented Jan 5, 2017

This pull request adds support for running the preload script and Electron APIs in a separate, isolated JavaScript context from the main JavaScript context of the loaded page.

This ensures the loaded page can't tamper with any JavaScript built-ins (such as Array.prototype.push, JSON.parse, etc.) that the preload script and Electron APIs make use of.

The preload script still has full access to the DOM, document, and window globals via secure proxies that prevent leakage across the contexts. This is provided by reusing Chrome's built-in support for content scripts.

This option is completely opt-in and no existing behavior is changed.

Example

Shown below is an application that loads a page (possibly remote/untrusted) but wants to open any clicked links in an external browser using Electron's shell API.

This example also shows how variables can be injected into the loaded page and how the preload script can listen for messages from the page using window.postMessage.

main.js

const {app, BrowserWindow} = require('electron')
const path = require('path')

let window

app.once('ready', () => {
  window = new BrowserWindow({
    webPreferences: {
      contextIsolation: true,
      preload: path.join(__dirname, 'preload.js')
    }
  })
  window.loadURL(`file://${__dirname}/index.html`)
})

preload.js

const {shell, webFrame} = require('electron')
const path = require('path')

// Set a variable in the page before it loads
webFrame.executeJavaScript('window.foo = "foo";')

// The loaded page will not be able to access this, it is only available
// in this context
window.bar = 'bar'

document.addEventListener('DOMContentLoaded', () => {
  // Will log out 'undefined' since window.foo is only available in the main
  // context
  console.log(window.foo)

  // Will log out 'bar' since window.bar is available in this context
  console.log(window.bar)

  document.body.addEventListener('click', (event) => {
    // Open clicked links externally
    if (event.target.tagName === 'A') {
      shell.openExternal(event.target.href)
      event.preventDefault()
    }
  })
})

window.addEventListener('message', (event) => {
  // Beep when the page requests it
  if (event.data === 'beep') {
    shell.beep()
  }
})

index.html

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
    <title>A Remote Page</title>
    <script>
      // Will log out 'foo' since window.foo is only available in this context
      console.log(window.foo)

      // Will log out 'undefined' since window.bar is only available in the isolated
      // context
      console.log(window.bar)
    </script>
  </head>
  <body>
    <a href="https://github.com">Click me</a>
    <button onclick="window.postMessage('beep', '*')">Beep!</button>
  </body>
</html>

Depends on electron/libchromiumcontent#251

/cc @electron/maintainers

@frozeman
Copy link
Copy Markdown

frozeman commented Jan 5, 2017

👍

@enlight
Copy link
Copy Markdown
Contributor

enlight commented Jan 6, 2017

The security guide should also be updated to mention this feature.

@kevinsawicki kevinsawicki force-pushed the isolated-world branch 4 times, most recently from 94317fd to 4454f49 Compare January 13, 2017 19:06
@kevinsawicki kevinsawicki merged commit feac868 into master Jan 17, 2017
@kevinsawicki kevinsawicki deleted the isolated-world branch January 17, 2017 16:47
@zeke
Copy link
Copy Markdown
Contributor

zeke commented Jan 17, 2017

Bravo, @kevinsawicki!

@kevinsawicki kevinsawicki mentioned this pull request Jan 17, 2017
@bundyo
Copy link
Copy Markdown
Contributor

bundyo commented Jan 20, 2017

Can we now get webviews with nodeIntegration off? :)

@MarshallOfSound
Copy link
Copy Markdown
Member

@bundyo You always disable nodeIntegration on a webview? Or do you mean use a webview from within a BrowserWindow which has nodeIntegration disabled?

@bundyo
Copy link
Copy Markdown
Contributor

bundyo commented Jan 20, 2017

The second. Our use case is that we don't want any node modules required by mistake in the renderer (and we also load some external content), so we preload them with nodeIntegration off. However, we do need a webview that has to communicate with the main process too. If we use an iframe, we should make an additional proxy from postMessage to ipc.

@MarshallOfSound
Copy link
Copy Markdown
Member

@bundyo Your use case sounds a bit unusual, however it is still not possible to load a webview in a webContents with nodeIntegration disabled.

However as a potential solution to your exact use case, have you considered simply using two WebViews? I.e.

Main Process
  |
  | - BrowserWindow
             | - WebView
             | - WebView

@bundyo
Copy link
Copy Markdown
Contributor

bundyo commented Jan 20, 2017

This actually sounds okay :) Thanks, I'll try it.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants