隔离模式
隔离模式是一种通过 JavaScript 拦截和修改前端发送给 Tauri Core 的 Tauri API 消息的方式,所有这些消息在到达 Tauri Core 之前都会被拦截和修改。隔离模式注入的安全 JavaScript 代码称为隔离应用程序。
原因
隔离模式的目的是为开发人员提供一种机制,帮助他们保护应用程序免受前端对 Tauri Core 发出的不需要或恶意的调用。隔离模式的需求源于运行在前端的不可信内容带来的威胁,对于具有许多依赖项的应用程序来说,这是一个常见的情况。请参阅 安全性:威胁模型,了解应用程序可能遇到的许多威胁来源的列表。
隔离模式在设计时考虑的最主要的威胁模型是开发威胁。许多前端构建时工具不仅包含几十个(或数百个)通常是深度嵌套的依赖项,而且一个复杂的应用程序还可能包含大量(也通常是深度嵌套的)依赖项,这些依赖项被捆绑到最终输出中。
时间
Tauri 强烈建议在任何可能使用隔离模式的情况下使用它。由于隔离应用程序拦截了来自前端的所有消息,因此它始终可以使用。
每当您使用外部 Tauri API 时,Tauri 还强烈建议锁定您的应用程序。作为开发者,您可以利用安全的 Isolation 应用程序来尝试验证 IPC 输入,以确保它们在一些预期参数内。例如,您可能需要检查对读取或写入文件的调用是否尝试访问应用程序预期位置之外的路径。另一个示例是确保 Tauri API HTTP 获取调用仅将 Origin 标头设置为您的应用程序期望的值。
也就是说,它会拦截前端的所有消息,因此它甚至可以与始终启用的 API(例如 事件)配合使用。由于某些事件可能会导致您自己的 rust 代码执行操作,因此可以使用相同类型的验证技术。
如何
Isolation 模式完全是关于在您的前端和 Tauri Core 之间注入一个安全应用程序,以拦截和修改传入的 IPC 消息。它通过使用 <iframe>
的沙盒功能来安全地运行 JavaScript,同时运行主前端应用程序。Tauri 在加载页面时强制执行 Isolation 模式,强制所有对 Tauri Core 的 IPC 调用首先通过沙盒 Isolation 应用程序进行路由。一旦消息准备好传递给 Tauri Core,它将使用浏览器的 SubtleCrypto 实现进行加密,并传回主前端应用程序。在那里,它直接传递给 Tauri Core,然后在那里解密并像往常一样读取。
为了确保某人无法手动读取特定版本应用程序的密钥,并用它在加密后修改消息,每次运行您的应用程序时都会生成新的密钥。
IPC 消息的近似步骤
为了便于理解,这里有一个有序列表,其中包含使用 Isolation 模式发送到 Tauri Core 时 IPC 消息将经历的近似步骤
- Tauri 的 IPC 处理程序收到一条消息
- IPC 处理程序 -> Isolation 应用程序
[sandbox]
Isolation 应用程序挂钩运行并可能修改消息[沙盒]
消息使用运行时生成的密钥通过 AES-GCM 加密[加密]
隔离应用程序 -> IPC 处理程序[加密]
IPC 处理程序 -> Tauri Core
注意:箭头 (->) 表示消息传递。
性能影响
由于确实会对消息进行加密,因此与 Brownfield 模式 相比,即使安全的隔离应用程序不执行任何操作,也会产生额外的开销成本。除了对性能敏感的应用程序(这些应用程序可能有一组经过精心维护且较小的依赖项,以保持足够的性能)之外,大多数应用程序应该不会注意到加密/解密 IPC 消息的运行时成本,因为这些成本相对较小,而 AES-GCM 相对较快。如果您不熟悉 AES-GCM,那么在此上下文中与之相关的就是它是 SubtleCrypto 中包含的唯一经过身份验证的模式算法,并且您可能已经在 TLS 的帮助下每天都在使用它。
每次启动 Tauri 应用程序时还会生成一个加密安全的密钥。如果系统已经拥有足够的熵以立即返回足够数量的随机数,则通常不会注意到这一点,这在桌面环境中非常常见。如果在无头环境中运行以执行一些 使用 WebDriver 进行集成测试,那么如果您使用的操作系统不包含此类服务,则可能需要安装某种熵生成服务,例如 haveged
。Linux 5.6(2020 年 3 月)现在包括使用推测执行的熵生成。
限制
由于平台不一致,隔离模式存在一些限制。最重大的限制是由于外部文件无法在 Windows 上的沙盒化 <iframes>
中正确加载。因此,我们在构建时实施了一个简单的脚本内联步骤,该步骤获取相对于隔离应用程序的脚本内容并将其内联注入。这意味着像 <script src="index.js"></script>
这样的典型捆绑或简单包含文件仍然可以正常工作,但 ES 模块等较新的机制将无法成功加载。
建议
由于隔离应用程序的目的是防范开发威胁,我们强烈建议尽可能保持隔离应用程序的简单性。您不仅应努力将依赖项保持在最少,还应考虑将所需的构建步骤保持在最少。这将使您不必担心针对隔离应用程序的供应链攻击,而您的前端应用程序则不必担心。
创建隔离应用程序
在此示例中,我们将制作一个小型的 hello-world 样式隔离应用程序,并将其连接到一个想象中的现有 Tauri 应用程序。它不会验证通过它的消息,只会将内容打印到 WebView 控制台。
出于此示例的目的,让我们想象我们与 tauri.conf.json
在同一目录中。现有 Tauri 应用程序已将其 distDir
设置为 ../dist
。
../dist-isolation/index.html
:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<title>Isolation Secure Script</title>
</head>
<body>
<script src="index.js"></script>
</body>
</html>
../dist-isolation/index.js
:
window.__TAURI_ISOLATION_HOOK__ = (payload) => {
// let's not verify or modify anything, just print the content from the hook
console.log('hook', payload)
return payload
}
现在,我们只需设置 tauri.conf.json
配置 以使用隔离模式,并从 Brownfield 模式 引导到隔离模式即可。
配置
让我们假设我们的主要前端 distDir
设置为 ../dist
。我们还将隔离应用程序输出到 ../dist-isolation
。
{
"build": {
"distDir": "../dist"
},
"tauri": {
"pattern": {
"use": "isolation",
"options": {
"dir": "../dist-isolation"
}
}
}
}