一分钟了解
- 你的服务器、登录凭据、密钥和笔记存放在你设备上的一个文件里,并使用 AES-256-GCM 加密。少数只针对本设备的选择,例如应用的外观和与 RDP 服务器共享的文件夹,以未加密的形式保存在应用的设置中,并且从不同步。
- 加密所用的密钥由你的配置文件密码通过 Argon2id 生成。Argon2id 是一种有意设计得很慢的方法,让猜测密码的代价很高。密码从不保存,也从不发送到任何地方。
- 如果你开启同步,每个条目在上传前都会先在你的设备上加密。服务器保存的是它无法解密的数据。
- 服务器确实能看到你数据的外围信息:你的邮箱地址,你在何时、从哪个 IP 地址同步,你每种条目各有多少个、各有多大,你的配置文件名称,以及共享配置文件的共享名称和成员的邮箱地址。
- 应用没有广告、没有统计分析,也没有崩溃报告。
服务器无法读取
- 你条目中的内容:名称、地址、端口、用户名、密码、私钥及其密码短语、隧道、笔记、命令、文件夹名称
- 哪个登录凭据或密钥属于哪台服务器
- 是谁修改了某个条目
- 你的配置文件密码、共享密码短语,以及能打开你配置文件的密钥
服务器能看到
- 你的邮箱地址,以及你登录时的账户密码(服务器只保存它的 bcrypt 哈希值)
- 登录和同步时的 IP 地址和时间
- 一个配置文件中每种条目各有多少个、它们的大小,以及每个条目在何时被哪台设备(以随机 ID 标识)修改
- 你的配置文件名称
- 对于共享配置文件:共享名称、成员的邮箱地址及其角色
同步时会发生什么
1. 在你的设备上
每个配置文件都保存为一个单独的文件,里面是一个小型数据库,存有你的服务器、登录凭据、SSH 密钥、笔记和设置。整个数据库先压缩,再用 AES-256-GCM 加密。每次保存文件时都会使用一个新的随机 nonce;而且文件会先写入一个临时副本,因此即使程序崩溃,也不会留下只写了一半的文件。
密钥由你的配置文件密码通过 Argon2id 生成,参数为:内存 64 MiB、3 轮迭代、4 条并行通道,并且每个配置文件都有各自的随机盐值。这些参数固定写在应用中,因此即使文件被改动,也无法削弱它们。
密码从不写入磁盘,也从不发送到任何地方。密钥生成后,应用核心中的那份密码副本会被清除;当你锁定配置文件时,密钥也会被清除。锁定还会关闭所有连接,并取消仍在进行中的登录。如果你忘了密码,这个配置文件就无法打开。也没有重置的办法,因为任何地方都没有保存能打开它的东西。
快速解锁是可选功能,默认关闭。在 Windows 上,它使用 Windows Hello 和 TPM;在 Android 上,它使用指纹传感器和 Android Keystore。系统保管的是配置文件的密钥,绝不是你的密码,而且这个密钥从不同步。在 Android 上,系统对本应用数据的云备份是关闭的。
在 Windows 上,当“远程桌面连接”打开一台保存了密码的服务器时,应用会通过一个临时的 .rdp 文件把密码交给它。文件中的密码使用 Windows DPAPI 针对你的 Windows 用户加密;“远程桌面连接”一读取完,文件就会被删除(最迟 30 秒后删除;如果程序崩溃留下了这个文件,会在下次启动时清除)。
- 密钥派生(Argon2id 参数) lib.rs:237–254
- AES-256-GCM 加密,每次使用新的 nonce lib.rs:256–263
- 保存配置文件 lib.rs:431–486
- 交给“远程桌面连接”的密码(DPAPI) rdp.rs:178–193
2. 云同步(Cloud sync)
在你登录之前,同步是关闭的。每个配置文件都有自己的随机 256 位数据密钥,它保存在加密的配置文件内部。上传前,每个条目(一台服务器、一个登录凭据、一个密钥、一条笔记……)都会用这个密钥单独加密,使用 AES-256-GCM 和一个新的随机 nonce。
除了加密条目本身,服务器还会收到每个条目的随机 ID、条目类型、一个时间戳,以及一个表示该条目是否已删除的标记。时间戳中包含做出更改的那台设备的随机 ID。从加密条目的大小,可以大致看出条目本身有多大。服务器还会保存每个配置文件的名称。已知主机密钥、命令历史、监控设置,以及只针对本设备的选择,从不同步。
以下是服务器为一个条目保存的内容 (示例)
{
"uuid": "6f1c2e0a9b7d4c3e8a5f0d1b2c3e4f50",
"entity_type": "servers",
"updated_at": "001761043562118:00000:9f3a61c2d4e5b7a8",
"deleted": false,
"blob": "4e8a1f0c2b9d3e7a5c6f8b1d0a2e4c6b8d0f1a3c5e7b9d1f…"
}3. 在新设备上打开配置文件
要在另一台设备上打开已同步的配置文件,那台设备需要该配置文件的数据密钥。因此,服务器还会多保存一条记录:经过加密的数据密钥。加密它的密钥由你的配置文件密码生成,使用相同的 Argon2id 参数。服务器无法打开它。
万一攻击者窃取了服务器的数据,这也是他们主要能尝试的事:离线猜测你的配置文件密码。每猜一次都要完整运行一次 Argon2id,因此,一个不在别处使用的长密码可以保护好你的数据。
- 配置文件密钥的存储副本 lib.rs:1551–1569
- 在新设备上打开它 lib.rs:1574–1594
4. 你的账户密码
你的同步账户有自己的密码,与配置文件密码分开。你登录时,它会通过 HTTPS 发送到服务器,服务器只保存它的 bcrypt 哈希值。输错 5 次密码后,密码登录会暂停 15 分钟。
该服务位于 Cloudflare 之后,由 Cloudflare 为它处理 HTTPS 连接。因此,和服务器一样,Cloudflare 在你登录时也可能看到你的账户密码;但对于你的条目,它只能看到加密后的形式。
切勿把配置文件密码用作账户密码。这样一来,服务器或 Cloudflare 能看到的任何内容,都打不开你的配置文件。
- 登录请求 cloud.rs:105–108
6. 如果服务器怀有恶意
好的安全性,不只看服务器本应做什么。下面列出了完全控制服务器的人能做和不能做的事。
他们无法
- 读取你的条目,或在不被发现的情况下修改其中的内容
- 在没有你的配置文件密码的情况下,打开存储的配置文件密钥副本
- 得知你的配置文件密码或共享密码短语,因为应用从不发送它们
- 更改你设备上的应用:应用没有自动更新,只会提示有新版本
他们可以
- 离线尝试猜测较弱的配置文件密码,每猜一次都要运行一次缓慢的 Argon2id
- 在你下次登录时截获你的账户密码
- 删除条目、恢复旧版本,或扣留更改。在同步删除任何内容之前,应用会在你的设备上保留一份配置文件副本
- 在你邀请他人加入共享配置文件时,提供错误的公钥
- 看到上面表格中列出的信息
7. 应用的全部网络连接
你自己的服务器,以及你为它们设置的代理和跳板机。同步服务器,仅在你已登录时连接。还有 GitHub,用于检查是否有新版本:在配置文件列表打开时,以及你点击“Check for updates”(检查更新)时,应用会查询 GitHub 上公开的发布版本列表。该请求只包含应用的名称和版本号。
应用中没有统计分析、崩溃报告或广告代码。同步时,应用只信任它内置的证书颁发机构列表,并且从不跟随重定向,因此无法被悄悄地指向别的服务器。
- 检查更新 about.rs:75–139
8. 远程桌面会从你的设备获得什么
剪贴板。你粘贴到 RDP 或 VNC 标签页中的文本会发送到那台服务器;在你粘贴之前,不会向它提供任何内容。开启 Share the clipboard(共享剪贴板)时(默认开启,每台服务器都有单独的开关),在远程桌面上复制的文本会被放到本设备的剪贴板上。只传输文本,从不传输文件,而且 VNC 只能传输纯 ASCII 文本。
文件:只有在电脑上使用内置 RDP 客户端,并且你为该服务器选择了一个文件夹或驱动器时,才会共享文件;你也可以设为只读。服务器看到的是 \\tsclient 下的一个驱动器,看不到你电脑上的其他任何内容。它请求的每个路径都会经过检查:不允许 ..,途中也不跟随任何链接,因此你所选范围之外的任何内容都无法访问。
这些选择保存在本设备上,从不同步,因此与你共享配置文件的人都无法开启它们。
- 把共享文件夹的路径限制在该文件夹之内 rdp_share.rs:121–147
- VNC 剪贴板可以传输的内容 vnc_client.rs:180–188
亲自验证
这些说法,你都不必只听我们的。下面是一些验证方法,由浅入深。
阅读代码
应用是开源的。本页中的链接直接指向实际完成这些工作的代码行,对应本页所描述的版本(
c9dec31)。观察网络连接
使用防火墙或网络监控工具。你会看到你自己的服务器、仅在你登录期间出现的同步服务器,以及配置文件列表打开时访问的 api.github.com。除此之外,再无其他。
查看服务器保存的内容
进阶:从应用的数据文件夹(cloud_token.json)中取出你的登录令牌,带上 X-Auth-Token 请求头向 API 请求你的数据:GET /api/sync/profiles,或者用空的记录列表发送 POST /api/sync。返回的只有 ID、类型、时间、标记和加密文本。完成后,请在应用中退出登录,以结束该会话。
查看通信内容
进阶:在你的电脑上运行一个小型代理,把请求转发给真正的服务器,然后把应用的同步服务器设为 http://127.0.0.1 加上代理的端口。应用只允许对你自己的电脑使用明文 HTTP。你会看到应用发送了什么,也会看到你的条目是加密的。(在系统中添加代理证书不起作用,因为应用使用自己的证书颁发机构列表。)
解密你自己的数据
进阶:有了你的密码,你就可以用任何支持 Argon2id 和 AES-GCM 的工具,按照下表中的参数打开你自己的数据。没有密码,谁都打不开。
校验下载文件
将校验和与发布版本中的 SHA256SUMS 进行比对(从 0.4 版开始提供),用 apksigner 检查 Android 应用的证书,或者从源代码自行构建应用。
供你自己的工具使用的参数
| 密钥派生 | Argon2id,版本 0x13,内存 64 MiB,3 轮迭代,4 条并行通道,32 字节密钥,16 字节随机盐值 |
| 本地配置文件 | “OMNV” · 版本 1 · 盐值(16 字节)· nonce(12 字节)· 用 AES-256-GCM 加密的、经 zstd 压缩的 SQLite 数据库,附带 16 字节标签 |
| 同步的条目 | hex(nonce(12 字节)· AES-256-GCM 密文和标签),使用配置文件的 256 位数据密钥 |
| 配置文件密钥的存储副本 | hex(0x02 · 盐值(16 字节)· nonce(12 字节)· 用 AES-256-GCM 加密的 32 字节数据密钥),使用由你的配置文件密码生成的密钥 |
| 共享 | X25519、HKDF-SHA256、AES-256-GCM |
本页不承诺什么
- Submarine 尚未经过独立的安全审计。迄今为止的审查都由作者本人完成。
- 弱密码或重复使用的密码是最薄弱的环节。请使用一个长且独一无二的配置文件密码,并使用一个与之不同的账户密码。
- Telnet 和普通 VNC 在设计上就不加密。应用会对它们加以标注,并且可以让它们经由 SSH 服务器连接。通过普通 VNC 时,你粘贴的或服务器复制的剪贴板文本,同样会以未加密的形式在网络上传输。
- 内置 RDP 客户端是实验性功能(它基于 IronRDP 的某个提交构建,而不是正式发布版本),在你为某台服务器选择它之前是关闭的。
- 本页描述的是提交
c9dec31对应的应用版本。代码如有变化,本页也会随之更新。