大家好,今天和大家讨论的是 Web 嵌入,无论是网站还是应用程序,在部分场景下我们需要嵌入一些第三方的 web 内容,例如我写了篇技术文章,其中部分包含视频内容,我上传到 B 站上了,我想把这段内容嵌入到我的技术文章中,就可能要使用 web 嵌入技术
在 Electron 中有三种方式可以让你在Electron的BrowserWindow
里集成(第三方)web内容,<iframe>
和, <webview>
和 WebContentsView
每个功能都略有不同,适用于不同的情况。
其实要是扣字眼的话,web嵌入范围会很大,一个 img
或 video
标签也可以算得上是 web 嵌入,今天讨论的 web 嵌入主要是嵌入第三方网站这类的操作
在 Electron
官方介绍中,并没有介绍在 iframe
之前出现的 web 嵌入技术 —— object
和 embed
,在 Java Applet
和 Flash
那个时代,它们的嵌入就是通过 object
和 embed
实现的
所以今天的文章中,我们都尝试一下,看看它们在 Electron
中是否还可以使用
https://www.electronjs.org/zh/docs/latest/tutorial/web-embeds
公众号开启了留言功能,欢迎大家留言讨论~
这篇文章也提供了 PDF
版本及 Github
,见文末
0x01 简介
0x02 iframe
1. iframe 属性
2. 渲染页面与 iframe 通信
3. iframe 执行 Node.js 的情况
4. iframe 上下文情况
5. 关闭同源策略
6. 小结
0x03 object
1. object 属性
2. 渲染页面与 object 通信及上下文
3. object 执行 Node.js 的情况
4. 关闭同源策略
5. object 和 iframe 的不同
6. object 标签内执行情况
7. 小结
0x04 embed
1. embed 属性
2. 渲染页面与 embed 通信及上下文
3. embed 执行 Node.js 的情况
4. embed 标签内执行情况
5. 小结
0x05 webview
1. 官方提示
2. 启用 webview
3. 应用效果
4. webview 属性
5. 渲染页面与 webview 上下文情况
6. webview 执行 Node.js 的情况
7. 总结
0x06 WebContentsView
1. 效果展示
2. WebContentsView 属性
3. 上下文和 Node.js
4. 小结
0x07 总结
0x08 PDF 版 & Github
往期文章
在之前的 nodeIntegrationInSubFrames
文章中,已经对 iframe
进行了部分介绍,这是一种现在通用的 web 嵌入方案,既然要加载第三方页面,那么肯定是允许跨域的,但跨域请求的地址受 CSP
策略的限制
关于 CSP
策略可以查看 CSP | Electron 安全 这篇文章
iframe
元素包含全局属性,也就是包含那些所有标签都可以使用的属性
用于为 iframe
指定一个权限策略,该策略定义哪些功能可用于(例如,访问麦克风、摄像头、电池、Web共享等)<iframe>
根据请求的来源。
权限策略的意义如下:
改变手机和第三方视频自动播放的默认行为
限制网站使用相机、麦克风、扬声器等敏感设备
允许 iframe 使用全屏 API
如果项目在视口中不可见,则停止对其进行脚本处理,以提高性能
权限策略 (Permissions-Policy
) 提供两种指定策略的方法:
HTTP 头 - Permissions-Policy
iframe
中的 allow
属性,当然控制的是 iframe
中使用的特性
权限策略采用继承制度,假如说页面的权限策略禁止访问麦克风,那么页面中嵌入的 iframe
会继承该策略,禁止使用麦克风,如果嵌入的 iframe
在 allow
属性中设置了自己的权限策略,那么就取子集
权限策略详细内容参考
https://developer.mozilla.org/zh-CN/docs/Web/HTTP/Permissions\_Policy
https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Permissions-Policy#iframes
设置为true
时,可以通过调用 <iframe>
的 requestFullscreen()
方法激活全屏模式
其实在上面的权限策略已经包含全屏模式的问题了,所以这是一个历史遗留属性
对嵌入的资源配置内容安全策略
以 CSS 像素格式,或像素格式,或百分比格式指定 frame 的高度。默认值为150
这是个实验性的属性,表示 <iframe>
的 src
属性指定的资源的加载优先级。允许的值有:
auto (default)
不指定优先级。浏览器根据自身情况决定资源的加载顺序
high
资源的加载优先级较高
low
资源的加载优先级较低
用于定位嵌入的浏览上下文的名称
该名称可以用作 a
标签与 form
标签的 target
属性值,也可以用作 input
标签和 button
标签的 formtarget
属性值,还可以用作 window.open()
方法的 windowName
参数值
表示在获取 iframe 资源时如何发送 referrer 头部
这个其实在之前文章 一次失败的漏洞串联尝试 中有提过,具体可以取值如下:
取值
含义
no-referrer
不发送 Referer
头
no-referrer-when-downgrade
(default)
向不受 TLS (HTTPS) 保护的 origin 发送请求时,不发送 Referer
头
origin
referrer 头中仅包含来源页面的源。换言之,仅包含来源页面的 scheme, host, 以及 port
origin-when-cross-origin
发起跨域请求时,仅在 referrer 中包含来源页面的源。发起同源请求时,仍然会在 referrer 中包含来源页面在服务器上的路径信息
same-origin
对于 same origin(同源)请求,发送 referrer 首部,否则不发送
strict-origin
仅当被请求页面和来源页面具有相同的协议安全等级时才发送 referrer 首部(比如从采用 HTTPS 协议的页面请求另一个采用 HTTPS 协议的页面)。如果被请求页面的协议安全等级较低,则不会发送 referrer 首部(比如从采用 HTTPS 协议的页面请求采用 HTTP 协议的页面)
strict-origin-when-cross-origin
当发起同源请求时,在 referrer 首部中包含完整的 URL。当被请求页面与来源页面不同源但是有相同协议安全等级时(比如 HTTPS→HTTPS),在 referrer 首部中仅包含来源页面的源。当被请求页面的协议安全等级较低时(比如 HTTPS→HTTP),不发送 referrer 首部
unsafe-url
始终在 referrer 首部中包含源以及路径(但不包括 fragment,密码,或用户名)。这个值是不安全的, 因为这样做会暴露受 TLS 保护的资源的源和路径信息
控制应用于嵌入在 <iframe>
中的内容的限制。该属性的值可以为空以应用所有限制,也可以为空格分隔的标记以解除特定的限制
这里必须注意,并不是说默认就开启 sandbox
,而是需要显式的设置 <iframe sandbox></iframe>
或 <iframe sandbox=""></iframe>
这个属性也是与我们关系比较大的内容,采用了默认即安全的配置方式
取值
含义
allow-downloads-without-user-activation
实验性: 允许在没有征求用户同意的情况下下载文件
allow-forms
允许嵌入的浏览上下文提交表单。如果没有使用该关键字,则无法提交表单
allow-modals
允许嵌入的浏览上下文打开模态窗口
allow-orientation-lock
允许嵌入的浏览上下文锁定屏幕方向(译者注:比如智能手机、平板电脑的水平朝向或垂直朝向)
allow-pointer-lock
允许嵌入的浏览上下文使用 Pointer Lock API.
allow-popups
允许弹窗 (例如 window.open, target="_blank", showModalDialog
)
如果没有使用该关键字,相应的功能将自动被禁用
allow-popups-to-escape-sandbox
允许沙箱化的文档打开新窗口,并且新窗口不会继承沙箱标记。例如,安全地沙箱化一个广告页面,而不会在广告链接到的新页面中启用相同的限制条件
allow-presentation
允许嵌入的浏览上下文开始一个 presentation session
allow-same-origin
如果没有使用该关键字,嵌入的浏览上下文将被视为来自一个独立的源,这将使 same-origin policy 同源检查失败
allow-scripts
允许嵌入的浏览上下文运行脚本(但不能创建弹窗)
如果没有使用该关键字,就无法运行脚本
allow-storage-access-by-user-activation
实验性: 允许嵌入的浏览上下文通过 Storage Access API 使用父级浏览上下文的存储功能
allow-top-navigation
允许嵌入的浏览上下文导航(加载)内容到顶级的浏览上下文
allow-top-navigation-by-user-activation
允许嵌入的浏览上下文在经过用户允许后导航(加载)内容到顶级的浏览上下文
备注: 当被嵌入的文档与主页面同源时,强烈建议不要同时使用
allow-scripts
和allow-same-origin
。如果同时使用,嵌入的文档就可以通过代码删除sandbox
属性,如此,就安全性而言还不如不用sandbox
。
如果攻击者可以在沙箱化的
iframe
之外展示内容,例如用户在新标签页中打开内联框架,那么沙箱化也就没有意义了。建议把这种内容放置到独立的专用域中,以减小可能的损失。沙箱属性 (sandbox) 在 Internet Explorer 9 及更早的版本上不被支持。
这其中 allow-scripts
、 allow-popups
、allow-popups-to-escape-sandbox
、allow-same-origin
、allow-top-navigation
与安全关系比较大
allow-scripts
是允许 iframe
嵌入的网页内部执行 JavaScript
,如果没有设置则不允许执行
我们测试一下,iframe
远程加载我们的页面 http://192.168.31.216/1.html
1.html
`<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> </head> <body> <div> <h1>iframe 页面 - 1.html</h1> <script> console.log(123) </script> </div> </body> </html> `
未显式这是 sandbox
iframe
内部的脚本可以成功执行
显式地设置 sandbox
当 sandbox
设置了 allow-scripts
时
这里有一个问题,未设置 sandbox
或 sandbox="allow-scripts"
时 iframe
中的 JavaScript
和 Electron
渲染页面的 JavaScript
是同一个上下文吗?
在渲染页面设置 window.flag = "success"
在 iframe
嵌入的内容中控制台输出 window.flag
进行测试
如果设置 contextIsolation: false
呢?
这也和之前文章介绍的一致,iframe
内部是一个独立的上下文
使用 srcdoc
执行也是一样的
allow-popups
是允许弹窗,这里的弹窗并不是 alert
函数这种,而是 window.open
打开的这种真的窗口
在 iframe
加载的内容中,使用 window.open
打开 https://www.baidu.com/
执行测试
window.open
的执行被拦截,因为默认不允许执行 JavaScript
,我们加上 allow-scripts
window.open
的执行还是被拦截了,我们添加 allow-popups
成功打开百度的页面
allow-popups-to-escape-sandbox
是让新窗口创建时,不会自动继承iframe
的 sandbox
,这可能会放宽安全措施
allow-same-origin
允许同源策略,可能部分朋友就蒙了,这些 sandbox
的选项不是在默认的限制中启用特权吗? 怎么还来了一个允许同源策略呢? 本来不就应该允许同源策略吗?
默认情况下,当一个 <iframe>
使用了 sandbox
属性而没有特别指定 allow-same-origin
时,该 <iframe>
中的文档会被视为来自一个独特的、无权限的源,即使实际上它与包含页面同源。这意味着即便内外页面同源,它们也不能直接互相访问DOM、Cookie或者使用localStorage等存储。
allow-top-navigation
比较重要,它用于控制嵌入在 <iframe>
中的页面是否有权限导航其顶层浏览上下文(即改变父窗口或顶级窗口的location)。默认情况下,当 sandbox
属性被应用时,这样的导航行为是被严格禁止的,以防止嵌入的内容对用户界面进行未经许可的修改,比如重定向主页面到恶意站点。
默认情况下,设置顶层导航 window.top.location.href = "https://www.baidu.com/"
设置导航失败,sandbox
属性中添加 allow-top-navigation
再次执行
一瞬间以后完成页面重载
被嵌入的页面的 URL 地址
使用 about:blank
值可以嵌入一个遵从同源策略的空白页。在 Firefox(version 65 及更高版本)、基于 Chromium 的浏览器、Safari/iOS 中使用代码移除 iframe
的 src
属性(例如通过 Element.removeAttribute()
)会导致 about:blank
被载入 frame。
对我们来说,比较重要的是 src
属性是否可以打开本地文件,是否会造成二进制文件等执行
Electron
中 iframe
的 src
属性可以使用本地文件 (可以加上 file://
) ,当然文件要在权限之内,例如读取 /etc/shadow
就会失败
测试一下是否可以触发二进制可执行程序的执行
Deepin Linux
在 Deepin Linux
多个版本测试后发现会触发下载行为,并不会直接执行
MacOS
结果与 Deepin Linux
一致
Windows 11
Deepin Linux
、MacOS
、Windows 11
上不同版本 Electron
表现一致,均为下载,而不是执行
该属性是一段 HTML 代码,这些代码会被渲染到 iframe 中。如果浏览器不支持 srcdoc
属性,则会渲染 src
属性表示的内容。
有了 src
,为何还要有一个 srcdoc
,甚至 srcdoc
中的内容可以直接被放到 iframe
中渲染,这多少有些奇怪,而且 srcdoc
属性还是一个相对新的属性,不是说历史遗留问题
srcdoc
相比于 src
的一个优势是不需要跨域,实际上就是一段 HTML 代码直接嵌入到 iframe
中,而不是让浏览器去加载一个外部的 URL
我们使用 Electron
测试一下
`<iframe srcdoc="<html><body>Hello, World!</body></html>"></iframe> `
Electron
是支持该语法的,在之前的 Electron
与你我息息相关的文章中其实就已经介绍了这个熟悉实现 RCE
等利用的内容
这里面的 JavaScript
也是可以执行的
如果同时设置了 src
和 srcdoc
会怎么样
`<iframe src="https://www.bilibili.com/" srcdoc="<html><body>Hello, World!</body></html>"></iframe> `
看来两者同时存在时以 srcdoc
优先,浏览器不支持 srcdoc
时才使用 src
以 CSS 像素格式,或以像素格式,或以百分比格式指定的 frame 的宽度。默认值是300
接下来的内容是不赞成使用的属性,可能不被所有的浏览器支持
此元素相对于周围元素的对齐方式
值为1
(默认值)时,显示此框架的边框。值为0
时移除边框。此属性已不赞成使用,请使用 CSS 属性 border
代替
表示框架内容的长描述的 URL。由于广泛的误用,该属性对于无图形界面的浏览器不起作用
从网络层面看,似乎 Electron
是不支持该属性的,几乎所有主流浏览器都不支持这个属性
这个属性定义了框架的内容距其上边框与下边框的距离,单位是像素
这个属性定义了框架的内容距其左边框和右边框的距离,单位是像素
这个属性控制是否要在框架内显示滚动条,允许的值包括:
auto: 仅当框架的内容超出框架的范围时显示滚动条
yes: 始终显示滚动条
no: 从不显示滚动条
这分为两种情况,渲染页面与 iframe
的地址同源和不同源
不同源的情况之前的文章就介绍过了,使用 postMessage
和 onMessage
进行通信
对于同源的情况,渲染进程访问 iframe
内变量的方式如下
iframe
页面设置变量
`window.flag = "strings for iframe" `
渲染进程可以使用 iframe
的 name
属性或者序号来获取 iframe
内部内容的 window
,因为我们只有一个 iframe
,所以序号为 0
`setTimeout(function() { console.log(window.frames[0]['flag']); }, 1000); `
这里设置了 1 秒的延时,保证 iframe
那边设置变量完成
这样就可以直接获取到 iframe
中的 window
对象了
同源的情况下 iframe
访问渲染进程就更简单了
渲染进程设置变量
`window.abc = "abc" `
iframe
内通过以下代码获取到变量
`window.parent.abc `
这里需要注意一点,大多数渲染进程的窗口是通过加载本地文件创建的,
本地文件创建的主窗口与 加载http(s)
页面的 iframe
是不同源的
本地文件创建的主窗口与加载本地文件的 iframe
是同源的
从上面的测试来看,iframe
肯定是不能直接执行 Node.js
,我们先来设置一下安全三大件
nodeIntegration: true
contextIsolation: false
sandbox: false
很遗憾,三大件还不够,再加一个 nodeIntegrationInSubFrames: true
nodeIntegration: true
contextIsolation: false
sandbox: false
nodeIntegrationInSubFrames: true
成功执行,那么是否可以缺少四个安全配置中的一个或几个呢?
经过测试,可以缺少 sandbox: false
,但是不能设置 sandbox: true
,默认配置是可以执行的,具体为什么参照 sandbox | Electron 安全 这篇文章
当大家看到这篇文章的时候,上面提到的 Electron
三大安全配置应该都在公众号上发表过了,大家可以想一下,我就为了让渲染进程或者渲染进程中的 iframe
执行个 Node.js
,为什么一定要关闭上下文隔离呢?
其实不难理解,当设置了
nodeIntegration
为true
的时候,其实就是单单给Preload
脚本开放了不受限制的Node.js API
访问能力,对于渲染进程的页面的上下文来说,是没有这个能力的但是如果此时
contextIsolation
被设置为了false
,也就是关闭了上下文隔离,那么渲染页面就可以访问到Preload
的上下文,获取到Preload
脚本中的window.require
了,进而可以直接调用require('child_process').exec('deepin-music')
如果开启上下文隔离,之后通过
contextBridge
将Preload
的require
暴露给渲染页面,对于我们攻击来说效果是一样的,毕竟我们只用到require
就够了
所以这里有些朋友可能会思考了,如果渲染进程页面和 iframe
的地址是同源的,那岂不是 iframe
内部直接可以通过下面的方式执行 Node.js
`window.parent.require('child_process').exec('deepin-music') `
这样的话,即使不开启 nodeIntegrationInSubFrames: true
是不是也可以执行 Node.js
?
成功执行 Node.js
代码
所以需要注意,不开启 nodeIntegrationInSubFrames
的情况下 iframe 内的代码也是可能可以执行 Node.js 的
通过上面的测试,这件事也比较清晰了,如果 iframe
的地址与渲染页面的地址不同源的话,那么 iframe
的上下文就是一个独立的上下文
如果 iframe
的地址与渲染页面的地址同源,并且关闭了上下文隔离, iframe
就可以通过 "找爹" 的方式获取到渲染页面的上下文,这里有一个问题,既然关闭了上下文隔离,是不是说 iframe
就可以一路找上去,获取到 Preload
脚本中的上下文呢?
测试一下
果然可以,一路畅通
如果 iframe
的地址与渲染页面的地址同源,但是开启了上下文隔离呢?
可以看到,此时 iframe
还是可以获取到渲染页面的上下文,但是无法获取到 Preload
脚本的上下文了
通过设置 webSecurity: false
关闭同源策略,情况会有不同吗?
并不会有什么变化
iframe
作为一种常用的嵌入方法,在 Electron
中也得到了很好的支持,iframe
支持 sandbox
属性,但是默认没有设置,需要显式地设置,sandbox
或 sandbox=""
表示开启所有限制,如果有特例允许的需求,可以在 sandbox
属性的值中设置,例如 sandbox="allow-scripts"
如果 iframe
的地址与渲染页面的地址同源,则可以相互直接通讯,并获取相互的上下文;如果非同源则不行,需要通过 postMessage
和 onMessage
进行通信
如果 iframe
的地址与渲染页面的地址同源,则在以下安全配置时,iframe
内可以执行 Node.js
nodeIntegration: true
contextIsolation: false
sandbox 没有显式地设置为 true
如果 iframe
的地址与渲染页面的地址不同源,则在以下安全配置时,iframe
内才可以执行 Node.js
nodeIntegration: true
contextIsolation: false
sandbox 没有显式地设置为 true
nodeIntegrationInSubFrames: true
如果 iframe
的地址与渲染页面的地址同源,并且关闭了上下文隔离,则 iframe
可以获取到渲染页面和 Preload
的上下文
如果 iframe
的地址与渲染页面的地址同源,但是关闭了上下文隔离,则 iframe
可以获取到渲染页面的上下文
如果 iframe
的地址与渲染页面的地址不同源,则 iframe
是一个独立的上下文
关闭同源策略 (webSecurity: false
) 并不会对上面的结果产生影响
参考文章
https://developer.mozilla.org/zh-CN/docs/Web/HTML/Element/iframe
HTML <object>
元素(或者称作 HTML 嵌入对象元素)表示引入一个外部资源,它用于嵌入各种外部对象到网页中,如图像、多媒体(如音频、视频)、SVG图形、PDF文档、Flash动画(虽然现代Web已逐步淘汰Flash)等。这个标签提供了一种灵活的方式来整合多种媒体类型和应用程序到网页中,而不局限于单一类型的资源。
通常使用语法如下
`<object data="path/to/resource" type="mime-type" width="width-value" height="height-value"> <!-- 可以在这里放置备用内容,供不支持object的浏览器或资源无法加载时显示 --> <param name="parameterName" value="parameterValue"> <!-- 设置对象参数 --> <embed src="path/to/resource" type="mime-type" width="width-value" height="height-value"> <!-- 有时用于兼容性增强 --> </object> `
可以看到,它也是支持加载 HTML 页面的
object
元素包含全局属性,也就是包含那些所有标签都可以使用的属性
用来指名对象资源列表的以空格分隔的 URI 列表
实测已被弃用
元素周围的边框的宽度,单位为像素
对象实现的 URI,可以同时与 data 属性使用,或者使用 data 属性替代
解析 classid,data 或者 archive 中定义的相对路径的根路径,如果没有定义,默认为当前文档的 base URI
classid 定义的 data 的内容类型
一个合法的 URL 作为资源的地址,需要为 data 和 type 中至少一个设置值
取值为布尔的属性可以设置这个元素为仅声明的格式。对象必须被随后的 <object> 元素实例化。在
HTML5 中,完整的重复 <object>
元素,可以重用元素
对象元素关联的 form 元素(属于的 form)。取值必须是同一文档下的一个 form 元素的 ID
资源显示的高度,单位是 CSS 像素
浏览上下文名称(HTML5),或者控件名称(HTML 4)
对象的实现和数据加载过程中,浏览器可以显示的信息
当前元素在文档 Tab 导航中的顺序
data 指定的资源的 MIME 类型,需要为 data 和 type 中至少一个设置值
指向一个 map
元素的 hash-name;格式为‘#’加 map 元素 name
元素的值
资源显示的宽度,单位是 CSS 像素
所以能用的也就剩下
data
form
height
name
type
width
<object>
元素自身并不直接提供一种标准化的跨上下文通信机制,类似 postMessage
但是我发现,渲染页面与 object
的 URL 同源的情况下还是渲染页面还是可以使用 window.frames
获取 object
的上下文
渲染页面成功获取到 object
的上下文
测试一下 object
是否可以通过 window.parent
的方式获取到渲染页面和Preload
的上下文
在开启上下文隔离的情况下,object
可以获取到渲染页面的上下文,但是无法获取 Preload
的上下文
显式地关闭上下文隔离,再次测试
object
成功获取到渲染页面以及 Preload
脚本的上下文
如果不同源,测试一下
被阻止
目前来看应该和 iframe
是一致的,测试一下
同源情况下
看来在同源情况下,object
想要执行 Node.js
,需要满足以下条件
nodeIntegration: true
contextIsolation: false
sandbox 没有显式地设置为 true
不同源的情况下,进行测试
执行失败,换成 window.parent.require
也失败
添加 nodeIntegrationInSubFrames: true
执行成功,开启上下文隔离或关闭 nodeIntegration
都会导致执行失败,所以不同源的情况下 object
执行 Node.js
的条件是:
nodeIntegration: true
contextIsolation: false
sandbox 没有显式地设置为 true
nodeIntegrationInSubFrames: true
如果关闭同源策略,会让不同源的 object
通过 window.parent
获取到渲染进程的上下文吗?
并不能
虽然 object
和 iframe
标签都是通过指定外部 URL 进行加载资源的,但是 iframe
标签内的内容不会被解析成HTML, objetc
标签内的内容会被解析成HTML
如果 object
标签内的内容和 data
属性都存在,会解析哪一个呢?
页面显示了 data
指定的内容,但是从页面 HTML 看,标签内的内容也解析了,我们换一个更加明显的 alert
object
标签内的情况就和一个 div
标签一样,并不是所谓的子 frame
,所以标签内的内容就是所谓的渲染页面
object
作为旧时代的嵌入,在 Electron
中也得到了很好的支持,测试效果与 iframe
基本一致
如果 object
的地址与渲染页面的地址同源,则可以相互直接通讯,并获取相互的上下文;如果非同源则需要依赖嵌入的内容自己的 API 是否支持通讯
如果 object
的地址与渲染页面的地址同源,则在以下安全配置时,object
内可以执行 Node.js
nodeIntegration: true
contextIsolation: false
sandbox 没有显式地设置为 true
如果 object
的地址与渲染页面的地址不同源,则在以下安全配置时,object
内才可以执行 Node.js
nodeIntegration: true
contextIsolation: false
sandbox 没有显式地设置为 true
nodeIntegrationInSubFrames: true
如果 object
的地址与渲染页面的地址同源,并且关闭了上下文隔离,则 object
可以获取到渲染页面和 Preload
的上下文
如果 object
的地址与渲染页面的地址同源,但是关闭了上下文隔离,则 object
可以获取到渲染页面的上下文
如果 object
的地址与渲染页面的地址不同源,则 object
是一个独立的上下文
关闭同源策略 (webSecurity: false
) 并不会对上面的结果产生影响
object
可以作为一个类似 div
的通用标签,内部的内容会当作正常的 HTML 渲染,data
和内部的代码同时存在时,data
部分正常执行,内部的 HTML 似乎不会渲染在页面上显示,但是内部的 JavaScript
会正常执行,执行限制和渲染页面策略一致,而不是和 data
指向的页面策略一致
参考文章
https://developer.mozilla.org/zh-CN/docs/Web/HTML/Element/object
HTML <embed>
元素将外部内容嵌入文档中的指定位置。此内容由外部应用程序或其他交互式内容源(如浏览器插件)提供,与 object
基本一致,只是更倾向于多媒体
资源显示的高度
被嵌套的资源的 URL
用于选择插件实例化的 MIME 类型
资源显示的宽度
就这么几个属性
直接测试
同源情况下
不同源
结果与 object
一致
结果与 object
一致
embed
与 object
不同的是,embed
标签内的内容渲染时会被放到和 embed
标签同级
embed
标签与object
标签表现基本一致,有一点不同的是,embed
标签内的内容渲染时会被放到和 embed
标签同级,而 object
标签内的内容渲染时会被放到 <object>
内部
参考文章
https://developer.mozilla.org/zh-CN/docs/Web/HTML/Element/embed
重要提示: 我们不建议您使用 WebView,因为这个标签会发生剧烈的结构变化,可能会影响您应用程序的稳定性。 考虑切换到其他选择,如
iframe
和Electron的BrowserView
,或避免嵌入式内容 设计的架构。
Electron
的 webview
标签基于 Chromium webview
,后者正在经历巨大的架构变化。 这将影响 webview
的稳定性,包括呈现、导航和事件路由。
默认情况下,Electron >= 5
禁用 webview
标签。 在构造 BrowserWindow
时,需要通过设置 webviewTag: true
来启用 webview
在一个独立的 frame 和进程里显示外部 web 内容
所以其实可以把它视为一个和主窗口一样的窗口进程看待
使用 webview
标签将'guest'内容 (例如网页) 嵌入到您的 Electron 应用中。 Guest 内容包含在 webview
容器内。 应用中的嵌入页面可以控制外来内容的布局和重绘。
与 iframe
不同, webview
独立于您的应用程序运行。 它拥有和你的页面不一样的权限并且所嵌入的内容和你应用之间的交互都将是异步的。 这将保证你的应用对于嵌入的内容的安全性。
注意: 从宿主页上调用 webview 的方法大多数都需要对主进程进行同步调用。
官方给的案例如下
`<webview id="foo" src="https://www.github.com/" style="display:inline-flex; width:640px; height:480px"></webview> `
它的属性大家会非常熟悉,因为和我们之前介绍的 BrowserWindow
中的 webPreferences
对应
`<webview src="https://www.github.com/"></webview> `
表示可见网址的 string
写入此属性将启动顶级跳转
更改 src
的值将重新加载当前页面。
src
属性还可以接受数据 URL, 如 data:text/plain, Hello, world!
。
`<webview src="https://www.google.com/" nodeintegration></webview> `
加载的页面是否集成 Node.js
`<webview src="https://www.google.com/" nodeintegrationinsubframes></webview> `
加载的页面内部的 iframe
等内容是否获取到 Preload
脚本暴露的内容
`<webview src="https://www.github.com/" plugins></webview> `
加载的页面是否可以使用浏览器插件
`<!-- 来自文件 --> <webview src="https://www.github.com/" preload="./test.js"></webview> <!-- 或从asar归档文件中加载 --> <webview src="https://www.github.com/" preload="./app.asar/test.js"></webview> `
预加载脚本
`<webview src="https://www.github.com/" httpreferrer="https://example.com/"></webview> `
为访客页面设置 referrer URL 的 string
`<webview src="https://www.github.com/" useragent="Mozilla/5.0 (Windows NT 6.1; WOW64; Trident/7.0; AS; rv:11.0) like Gecko"></webview> `
设置加载页面时使用的 User-Agent
`<webview src="https://www.github.com/" disablewebsecurity></webview> `
关闭安全策略,默认是开启安全策略的,只有当设置 disablewebsecurity
时才关闭安全策略
`<webview src="https://github.com" partition="persist:github"></webview> <webview src="https://electronjs.org" partition="electron"></webview> `
设置页面使用的会话的 string
。
如果 partition
以 persist:
开头, 该页面将使用持续的 session,并在所有页面生效,且使用同一个partition
. 如果没有 persist:
前缀, 页面将使用 in-memory session. 通过分配相同的 partition
, 多个页可以共享同一会话。 如果没有设置partition
,app 将会使用默认的session。
`<webview src="https://www.github.com/" allowpopups></webview> `
如果该属性存在,加载的页面将允许打开新窗口。 Popup 默认是禁用状态
`<webview src="https://github.com" webpreferences="allowRunningInsecureContent, javascript=no"></webview> `
string
是一个由逗号分割的字符串列表,其中指定了要设置在 webview 上的 Web 首选项。 支持的首选项字符串的完整列表,请查看 BrowserWindow
此外,webview
的 webpreferences
还支持以下字符串
transparent
boolean (optional) - 加载的页面是否使用透明背景`<webview src="https://www.github.com/" enableblinkfeatures="PreciseMemoryInfo, CSSVariables"></webview> `
一个字符串列表,表示要启用的 blink
特性,这是指启用 blink
引擎的特性,属于 Chromium
的范畴
`<webview src="https://www.github.com/" disableblinkfeatures="PreciseMemoryInfo, CSSVariables"></webview> `
禁用 blink
引擎特性的列表
从上面的内容大家可以看出,webview
就相当于一个 BrowerWindow
,所以它还包含大量的方法和 DOM 事件,量太大了,不一一列举了,具体可以看下方参考文章
从官方描述来看,应该是没有上下文关联的,但是我们还是试试
即使是安全策略全都关掉, webview
本身还是独立的上下文
经过测试,只有当 BrowerWindow
设置为
webviewTag: true
nodeIntegration: true
contextIsolation: false
sandbox: false 或保持默认不设置
的情况下,webview
的那些选项才可以生效,nodeIntegrationInSubFrames
并不影响 webview
本身执行 Node.js
在此基础上,webview
进行如下配置,加载的页面即可执行 Node.js
`<webview id="foo" src="./1.html" style="display:inline-flex; width:640px; height:480px" nodeintegration webpreferences="contextIsolation=false"></webview> `
webview
配置中缺少关闭上下文隔离都不行
webview
加载页面是一个独立的上下文,想与渲染进程或主进程通信需要使用 IPC ,webview
中的页面想要执行 Node.js
的前提是,外部的渲染进程可以执行 Node.js
并且还要加上 webview
自己的配置才可以
`<webview nodeintegration webpreferences="contextIsolation=false"></webview> `
参考文章
https://www.electronjs.org/zh/docs/latest/tutorial/web-embeds#webview
https://www.electronjs.org/zh/docs/latest/api/webview-tag#warning
WebContentsView
和 BaseWindow
在 Electron 30.0
中添加,用来废弃并替换 BrowserView
,它们是主进程模块,也就是说比 webview
更高级一层,不是渲染进程的一部分,而是由主进程直接进行管理
WebContentsView
可以加载一个页面,多个 WebContentsView
可以放入到一个 BaseWindow
中进行合并、分层等管理
该模块需要在 app 的 ready
事件之后运行才有效果
`const { BaseWindow, WebContentsView } = require('electron') const win = new BaseWindow({ width: 800, height: 400 }) const view1 = new WebContentsView() win.contentView.addChildView(view1) view1.webContents.loadURL('https://electronjs.org') view1.setBounds({ x: 0, y: 0, width: 400, height: 400 }) const view2 = new WebContentsView() win.contentView.addChildView(view2) view2.webContents.loadURL('https://github.com/electron/electron') view2.setBounds({ x: 400, y: 0, width: 400, height: 400 }) `
这里我们将 createWindow()
注释掉了,因为从效果看, BaseWindow
和 BrowerWindow
效果是一个类型的,算是整体的窗口容器吧,而 WebContentsView
是在其中嵌入的内容
WebContentsView
只有一个属性,就是 webPreferences
,就是我们创建 BrowserWindow
时传递安全配置那些
https://www.electronjs.org/docs/latest/api/structures/web-preferences
因为 WebContentsView
是主进程模块,按照上面的案例,每个 WebContentsView
是有一个实例对象的,可以通过实例对象对其进行管理
是否能够执行 Node.js
主要取决于WebContentsView
创建实例时 webPreferences
的配置了
从 Electron 30.0
开始 WebContentsView
替换了 BrowserView
,配合 BaseWindow
可以有效进行集成管理,WebContentsView
创建实例对象时,可以指定 webPreferences
进行安全配置,其中配置选项和 BrowserWindow
一致,是否可以执行 Node.js
等完全取决于 webPreferences
参考文章
https://www.electronjs.org/zh/blog/electron-30-0#%E9%87%8D%E7%82%B9%E5%86%85%E5%AE%B9
https://www.electronjs.org/zh/docs/latest/api/web-contents-view#class-webcontentsview-extends-view
本篇文章总共介绍了三种web嵌入技术,细数共 5 个
第一种包括 iframe
、object
、embed
,也就是 HTML 原生支持的技术,这个在 Electron
中得到了很好的支持,对于它们仨,上下文及 Node.js
执行能力的条件基本是一致的,具体与同源还是不同源有很大关系,以 iframe
为例来说
如果 iframe
的地址与渲染页面的地址同源,则在以下安全配置时,iframe
内可以执行 Node.js
nodeIntegration: true
contextIsolation: false
sandbox 没有显式地设置为 true
如果 iframe
的地址与渲染页面的地址不同源,则在以下安全配置时,iframe
内才可以执行 Node.js
nodeIntegration: true
contextIsolation: false
sandbox 没有显式地设置为 true
nodeIntegrationInSubFrames: true
第二种是 webview
,属于是来源于 Electron
(相对 web 三大件来说),存在于渲染页面之中,webview
加载页面是一个独立的上下文,想与渲染进程或主进程通信需要使用 IPC
webview
中的页面想要执行 Node.js
的前提是,外部的渲染进程可以执行 Node.js
并且还要加上 webview
自己的配置才可以
`<webview nodeintegration webpreferences="contextIsolation=false"></webview> `
第三种是 WebContentsView
,属于是 Electron
主进程中管理的模块,从 Electron 30.0
开始,它替代了 BrowserView
,与 BaseWindow
组合,达到 web 嵌入的效果
WebContentsView
创建实例对象时,可以指定 webPreferences
进行安全配置,其中配置选项和 BrowserWindow
一致,是否可以执行 Node.js
等完全取决于 webPreferences
PDF版
Github
有态度,不苟同