Skip to content

Commit cf635c5

Browse files
docs: add document on contextIsolation (electron#23474)
* docs: add document on contextIsolation * fix lint * chore: link ctx isolation doc from security doc
1 parent 653c36b commit cf635c5

File tree

2 files changed

+73
-44
lines changed

2 files changed

+73
-44
lines changed

docs/tutorial/context-isolation.md

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
# Context Isolation
2+
3+
## What is it?
4+
5+
Context Isolation is a feature that ensures that both your `preload` scripts and Electron's internal logic run in a separate context to the website you load in a [`webContents`](../api/web-contents.md). This is important for security purposes as it helps prevent the website from accessing Electron internals or the powerful APIs your preload script has access to.
6+
7+
This means that the `window` object that your preload script has access to is actually a **different** object than the website would have access to. For example, if you set `window.hello = 'wave'` in your preload script and context isolation is enabled `window.hello` will be undefined if the website tries to access it.
8+
9+
Every single application should have context isolation enabled and from Electron 12 it will be enabled by default.
10+
11+
## How do I enable it?
12+
13+
From Electron 12, it will be enabled by default. For lower versions it is an option in the `webPreferences` option when constructing `new BrowserWindow`'s.
14+
15+
```javascript
16+
const mainWindow = new BrowserWindow({
17+
webPreferences: {
18+
contextIsolation: true
19+
}
20+
})
21+
```
22+
23+
## Migration
24+
25+
> I used to provide APIs from my preload script using `window.X = apiObject` now what?
26+
27+
Exposing APIs from your preload script to the loaded website is a common usecase and there is a dedicated module in Electron to help you do this in a painless way.
28+
29+
**Before: With context isolation disabled**
30+
31+
```javascript
32+
window.myAPI = {
33+
doAThing: () => {}
34+
}
35+
```
36+
37+
**After: With context isolation enabled**
38+
39+
```javascript
40+
const { contextBridge } = require('electron')
41+
42+
contextBridge.exposeInMainWorld('myAPI', {
43+
doAThing: () => {}
44+
})
45+
```
46+
47+
The [`contextBridge`](../api/context-bridge.md) module can be used to **safely** expose APIs from the isolated context your preload script runs in to the context the website is running in. The API will also be accessible from the website on `window.myAPI` just like it was before.
48+
49+
You should read the `contextBridge` documentation linked above to fully understand its limitations. For instance you can't send custom prototypes or symbols over the bridge.
50+
51+
## Security Considerations
52+
53+
Just enabling `contextIsolation` and using `contextBridge` does not automatically mean that everything you do is safe. For instance this code is **unsafe**.
54+
55+
```javascript
56+
// ❌ Bad code
57+
contextBridge.exposeInMainWorld('myAPI', {
58+
send: ipcRenderer.send
59+
})
60+
```
61+
62+
It directly exposes a powerful API without any kind of argument filtering. This would allow any website to send arbitrary IPC messages which you do not want to be possible. The correct way to expose IPC-based APIs would instead be to provide one method per IPC message.
63+
64+
```javascript
65+
// ✅ Good code
66+
contextBridge.exposeInMainWorld('myAPI', {
67+
loadPreferences: () => ipcRenderer.invoke('load-prefs')
68+
})
69+
```
70+

docs/tutorial/security.md

Lines changed: 3 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -239,51 +239,10 @@ to enable this behavior.
239239
Even when you use `nodeIntegration: false` to enforce strong isolation and
240240
prevent the use of Node primitives, `contextIsolation` must also be used.
241241

242-
### Why?
243-
244-
Context isolation allows each of the scripts running in the renderer to make
245-
changes to its JavaScript environment without worrying about conflicting with
246-
the scripts in the Electron API or the preload script.
247-
248-
While still an experimental Electron feature, context isolation adds an
249-
additional layer of security. It creates a new JavaScript world for Electron
250-
APIs and preload scripts, which mitigates so-called "Prototype Pollution" attacks.
251-
252-
At the same time, preload scripts still have access to the `document` and
253-
`window` objects. In other words, you're getting a decent return on a likely
254-
very small investment.
255-
256-
### How?
242+
### Why & How?
257243

258-
```js
259-
// Main process
260-
const mainWindow = new BrowserWindow({
261-
webPreferences: {
262-
contextIsolation: true,
263-
preload: path.join(app.getAppPath(), 'preload.js')
264-
}
265-
})
266-
```
267-
268-
```js
269-
// Preload script
270-
271-
// Set a variable in the page before it loads
272-
webFrame.executeJavaScript('window.foo = "foo";')
273-
274-
// The loaded page will not be able to access this, it is only available
275-
// in this context
276-
window.bar = 'bar'
277-
278-
document.addEventListener('DOMContentLoaded', () => {
279-
// Will log out 'undefined' since window.foo is only available in the main
280-
// context
281-
console.log(window.foo)
282-
283-
// Will log out 'bar' since window.bar is available in this context
284-
console.log(window.bar)
285-
})
286-
```
244+
For more information on what `contextIsolation` is and how to enable it please
245+
see our dedicated [Context Isolation](context-isolation.md) document.
287246

288247

289248
## 4) Handle Session Permission Requests From Remote Content

0 commit comments

Comments
 (0)