当你有nodeIntegration
已禁用但未使用contextIsolation
,您可以使用预加载脚本在全局对象上公开它的安全版本。 (注意:你不应该暴露整个fs
模块到远程页面!)
以下是以此方式使用预加载脚本的示例:
// main process script
const mainWindow = new BrowserWindow({
webPreferences: {
contextIsolation: false,
nodeIntegration: false,
preload: './preload.js'
}
})
mainWindow.loadURL('my-safe-file.html')
// preload.js
const { readFileSync } = require('fs')
// the host page will have access to `window.readConfig`,
// but not direct access to `readFileSync`
window.readConfig = function () {
const data = readFileSync('./config.json')
return data
}
// renderer.js
const config = window.readConfig()
如果您仅加载本地页面,并且这些页面不加载或执行不安全的动态内容,那么您might重新考虑使用contextIsolation
对于这个策略。如果你想保留contextIsolation
但是,如果您有机会显示不安全的内容,您绝对应该这样做),您只能与预加载脚本进行通信消息通过postMessage.
这是上面相同场景的示例,但是contextIsolation
并使用消息传递。
// main process script
const mainWindow = new BrowserWindow({
webPreferences: {
contextIsolation: true,
nodeIntegration: false,
preload: './preload.js'
}
})
mainWindow.loadURL('my-unsafe-file.html')
// preload.js
const { readFileSync } = require('fs')
const readConfig = function () {
const data = readFileSync('./config.json')
return data
}
window.addEventListener('message', (event) => {
if (event.source !== window) return
if (event.data.type === 'request') {
window.postMessage({ type: 'response', content: readConfig() })
}
})
// renderer.js
window.addEventListener('message', (event) => {
if (event.source !== window) return
if (event.data.type === 'response') {
const config = event.data.content
}
})
window.postMessage('request')
虽然这肯定更冗长且难以处理(并且强制事情异步,因为消息传递是异步的),但它也更安全。一对小的 JS 包装器围绕postMessage
API 可以使这更容易使用(例如通过类似 RPC 的机制),但请记住使用的全部意义contextIsolation
是因为你不能信任渲染器,所以你的预加载脚本不应该信任它通过渲染器获得的任何消息postMessage
API — 您应该始终验证收到的事件以确保您信任它。
这个幻灯片详细描述了为什么在不使用上下文隔离的情况下关闭节点集成并不总是一个好主意。