跳到正文

目录

Croc:一行命令背后的端到端加密文件传输

Croc:一行命令背后的端到端加密文件传输

Croc 解决的是"如何在两台任意电脑之间安全地传文件"这个老问题——不需要公网 IP、不需要端口映射、不需要注册账号、不需要把文件交给第三方服务器,同时保证端到端加密。

在它之前,大多数方案都有明显的短板:scp/sftp 需要双方能直连;WeTransfer 之类的网页工具要把文件上传到中间服务器;局域网传输工具出了内网就用不了。Croc 是 Go 写的命令行工具,作者 schollz,MIT 协议完全开源,2017 年 10 月建库,至今 40,500+ star。设计上明确致谢了 Magic Wormhole——口令认证、中继穿墙这个思路一脉相承,但 Croc 把入口压缩到了一行命令。

系统地图:Croc 的四层技术架构

层级职责关键技术
用户层交互与发现一串短助记词构成的 code phrase
加密层密钥协商与加密PAKE 协议 + 认证加密
传输层建立数据通道P2P 直连优先,中继回退,浏览器走 WebSocket
协议层传输控制逻辑断点续传、多文件、进度管理

这四层设计的要点是:用户只需要记住一串短助记词,就能在底层拿到完整的加密保障——不需要理解密码学,也不需要手动交换密钥。

四层的边界划分是刻意的:加密层负责"安全",传输层只负责"转发"。两层互不耦合,决定了整个系统的信任模型(下文详述)。

为什么 PAKE 是这项技术的核心

Croc 使用 PAKE(Password Authenticated Key Exchange,密码认证密钥交换) 协议,这是它与其他文件传输工具最大的区别。

传统的密钥交换有两种模式:

  1. 公钥加密:双方需要先知道对方的公钥,但如何安全地交换公钥本身就是个问题
  2. 对称加密:需要一个共享密钥,但如何在不安全的信道上安全地交换密钥?

PAKE 解决的就是这个问题:双方只需要共享一个短口令,就可以在协议层协商出一个安全的会话密钥,而攻击者即使截获了所有流量,也无法获得这个密钥。

更关键的一点:即使口令强度不高,PAKE 也能让离线暴力破解失效——协议的每一轮猜测都必须与通信对端做一次真实交互,攻击者无法把抓到的报文拿回去离线穷举。“破解口令"从"算得快就行"变成"必须在线上一次次试探”,攻击代价大幅上升。

这就是为什么 Croc 只需要几个助记词就能达到很高的安全性——不是因为口令本身难以猜测,而是协议把暴力破解限制在了在线、逐次进行的量级。Croc 的实现是 schollz 自己的 pake 库(v3),口令协商出的密钥再喂给认证加密层。

核心机制拆解

1. 连接建立:P2P 优先,中继回退

早期版本的 Croc 数据一律过中继;v11 把默认行为改成了先试直连:

  • 双方完成 PAKE 握手后,创建绑定本次口令的 Tailcat 节点身份,在一个进程内的 Tailscale 用户态 WireGuard 网络里开 TCP 流
  • Magicsock 先走 DERP 中转起步,NAT 穿透成功后自动升级为直连 UDP
  • 对端是浏览器、客户端过旧、或 Tailcat 建不起来时,回退到传统的 croc 中继数据端口

--transport 参数可以控制这条路:默认 auto,derp 严格模式下要求 Tailcat 可用并禁用中继回退,relay 强制走 SOCKS5 兼容的中继传输(配合 --socks5 用于只放行代理流量的网络)。传输怎么选,加密都不变——WireGuard 通道和加密消息是两层。

无论走哪条路,两端都不需要知道对方 IP,也不需要配置任何端口映射。

2. 端到端加密流程

一次完整的加密传输流程是这样的:

  1. 发送方和接收方通过 PAKE 协议协商出共享会话密钥(双方都不需要暴露任何长期密钥)
  2. 发送方用认证加密算法加密文件内容
  3. 加密后的数据经直连通道或中继转发
  4. 接收方用会话密钥解密,并校验完整性与真实性
  5. 每次传输都重新协商密钥,会话结束即作废

整个过程中,未加密的明文永远不会离开发送方和接收方的机器。还有一个容易被忽略的细节:文件元信息(文件名、大小)也走加密通道。源码里所有协议消息——包括声明文件列表的 fileinfo 消息——都用会话密钥加密编码后才发出去,中继只能看到密文,拿不到文件名。

3. 断点续传与多文件支持

Croc 支持在传输中断后从断点恢复,而不需要从头开始:

  • 接收方对已存在的同大小文件计算哈希,与发送方比对,一致就跳过
  • 如果接收方已有部分数据,通知发送方从剩余处继续
  • 传输进度实时显示在终端上

同时支持一次传输多个文件或整个文件夹。对超大文件而言,断点续传意味着几十 GB 也不会因为一次断网就前功尽弃。默认哈希算法够用,赶时间可以用 croc send --hash imohash 换快速哈希——代价是哈希碰撞概率略高,适合"大致校验"的场景。

4. IPv6 优先与代理支持

Croc 默认 IPv6 优先、IPv4 兜底。同时支持通过 --socks5 走 SOCKS5 代理(例如 Tor),满足更高的匿名性需求——注意代理要两端都设(或用 SOCKS5_PROXY 环境变量),中继域名也由代理解析,本机连 DNS 都可以不配。代理只负责传输路径,文件内容依旧是端到端加密的。

5. 加密暂存:croc store(v11 新增)

双方不方便同时在线时,v11 提供了客户端加密的暂存传输:

croc store file1.zip --downloads 3 --expiration 3d

文件以密文上传到存储服务,命令打印一个浏览器链接和一个 CLI token。链接形如 https://host/s/id#v1.decryption-key——解密密钥放在 # 之后,URL fragment 不会进入 HTTP 请求,所以存储服务只拿到不透明的传输 ID,拿不到密钥。下载次数(默认 1 次)和有效期(默认 1 天,支持 m/h/d/w)任一耗尽即失效;发送方可以凭本地保存的回执执行 croc --revoke 随时撤销。

这个模式是可选项,和常规的实时中继传输是两条路。要注意完整链接本身就是秘密:谁拿到链接,谁就能解密文件并消耗下载次数。

6. 共享终端:croc ssh(v11 新增)

v11 还把"传东西"扩展到了"传终端":

croc ssh

在 Linux、macOS、FreeBSD、OpenBSD 上,宿主运行这条命令后会拿到两份六词邀请:一份读写、一份只读。参与者用 CROC_SECRET='six-word-invitation' croc ssh 加入(继续避开进程名泄露,后述),所有人看到同一个持久终端,只读参与者的输入会被丢弃。

它不暴露 SSH 守护进程,不需要账号、公网 IP、入站端口或 SSH 密钥:邀请认证一条临时的 Tailcat WireGuard 路径并钉扎临时 SSH 主机密钥;Tailcat 不可用时,回退到普通 croc 中继承载加密流。远程命令执行、端口转发和 SFTP 都被禁用。两份邀请都按秘密对待——邀请上印着角色,谁拿到就能以对应权限加入。

常用命令速查

以下命令均可在发送或接收端灵活组合:

场景命令
发送文件 / 文件夹croc send [文件或文件夹]
接收croc [code-phrase]
自定义口令(至少 6 字符)croc send --code [口令] [文件]
一次发多个文件 / 文件夹croc send [file1] [file2] [folder]
发送一段文本croc send --text "hello"
通过管道收发cat file | croc send / croc --yes [code] > out
生成二维码(手机扫码即收)croc send --qr [文件]
覆盖同名文件croc --yes --overwrite [code]
改名保存同名文件croc --yes --rename [code]
排除子串匹配的路径croc send --exclude "node_modules,.venv" [folder]
精确排除某个相对路径croc send --exclude-file "sub/a.log" [folder]
静默模式(脚本用)croc --quiet send [file]
走 SOCKS5 代理croc --socks5 "127.0.0.1:9050" send [file]
换加密曲线croc --curve p521 [code]
环境变量传口令(Linux/macOS)CROC_SECRET=口令 croc

两个排除参数的区别值得说一句:--exclude 对每个文件的相对路径做大小写不敏感的子串匹配,路径里任何位置含给定字符串就排除;--exclude-file(v11 新增)按相对路径精确匹配,用于"两个不同深度的同名文件只排除其中一个"这类场景。

code phrase:从一个数字加四个词,到三个词

接收凭证 code phrase 本身也经历了一次重构。

v10 时代的自动生成格式是"4 位数字 + 4 个助记词"(mnemonicode 词表),例如 1234-tomato-acrobat-actor-silver 这样的四段式。v11 换成了 EFF 词表(1,296 词)生成的三词 code:第一个词定位中继房间,后两个词充当 PAKE 口令;croc ssh 的邀请则用六个词——两个词选房间、四个词进口令,熵更高。自定义 code 仍然只要求至少 6 个字符,新旧格式都能解析。

三词 code 还承担了一个新职责:公共中继路由。默认公共传输按 code 的 SHA-256 对三中继池取模决定用哪个部署;自动生成的发送端会探测全部三个中继、选第一个应活的;自定义 code 则直接映射、不探测不回退。探测选中的中继地址缓存进本地 best-relay 文件,下次直接复用;连接失败就清掉缓存重新探测。指定 --relay 或 CROC_RELAY、--ip、本地传输都会绕过这套公共路由。

自建中继:把信任握在自己手里

Croc 默认使用公开中继,但既然完全开源,也支持自建:

croc relay
  • 默认监听 TCP 端口 9009-9013,至少需要 2 个端口
  • 用 croc relay --ports 1111,1112 自定义端口范围
  • 发送方 / 接收方通过 --relay "myrelay.example.com:9009" 指定自己的中继
  • Docker 一行起服务:docker run -d -p 9009-9013:9009-9013 -e CROC_PASS='YOURPASSWORD' docker.io/schollz/croc,客户端配 --pass 对接

自建中继的价值有二:一是内网或隔离网络里无法访问公开中继时自建出口;二是把"转发流量"这一环收进自己手里。需要说明的是,自建中继依然看不到明文——它只是把加密的传输层也从默认服务商那里收回来而已,这正好检验了前面那句"加密层与传输层互不耦合"。

一个典型的文件传输流程

发送方 A 给接收方 B 传一个大文件:

  1. A 在终端输入 croc send big-file.zip
  2. Croc 生成三词 code,如 briny-gadget-quantum(示意)
  3. A 通过聊天工具把这个 code 发给 B
  4. B 在终端输入 croc briny-gadget-quantum
  5. A 和 B 通过 PAKE 在本地协商出共享会话密钥(中继全程看不到这个密钥)
  6. 双方先尝试 Tailcat 直连;穿透失败则回落中继,A 的加密数据开始流动
  7. B 接收加密数据,在本地解密,实时显示进度
  8. 如果网络中断,任何一方重新执行命令即可从断点继续
  9. 传输完成,本次会话密钥作废

全程 A 和 B 都不需要知道对方的 IP 地址,也不需要配置任何网络参数。

安全与隐私约定

这里把 Croc 的安全性质明确为四条"约定",方便你判断它在自己的场景下是否够用:

  • 中继无知情权:中继只能看到密文,拿不到文件名、内容和会话密钥;被攻破也不泄露文件。
  • 信任锚是口令:code phrase 是唯一的访问凭证。它应通过可信信道(如加密聊天)传递,且不要反复使用同一个口令,避免在线上为攻击者堆积试探机会。
  • 进程列表也会泄露秘密:在 Linux / macOS 上,若把 code phrase 直接作为命令行参数传入,它会出现在进程列表中(安全公告 CVE-2023-43621)。官方要求改用环境变量传入(CROC_SECRET=... croc);单用户系统可以运行 croc --classic 永久恢复传统行为。croc --extended-clipboard 会把带 CROC_SECRET= 的完整命令复制进剪贴板,省去手敲。
  • 直连优先,中继兜底:理想网络下双方建立端到端直连(Tailcat WireGuard,NAT 穿透成功后为直连 UDP),中继只在回退时介入,数据经过中间节点的机会被压到最低。

另外两条来自 v11 新功能的提醒:croc ssh 的两份邀请同 code phrase 一样是秘密;croc store 的完整链接(含 # 后的密钥)也一样——链接发给谁,等于把文件发给谁。

适用边界与采用建议

谁应该优先使用

  • 需要跨平台传文件的人:Windows、Linux、Mac 之间互传,再也不用找 U 盘。
  • 需要传大文件的人:断点续传意味着几十 GB 的文件也不怕断网。
  • 注重隐私的用户:文件不在第三方服务器明文落地,只有发送方和接收方能解密。
  • CLI 用户:终端完成所有操作,也方便脚本化。

谁可以暂时不用

  • 只在同一局域网内传输:用 --ip 直接指定发送方地址(如 10.0.0.1:9009)可以建立点对点直连,无需依赖中继。
  • 传输极度敏感的数据:PAKE 协议很安全,但极端敏感场景可能还是需要物理介质。
  • 完全不会用命令行的用户:现在的选择比以前多了——浏览器版 getcroc.com 已转正为正式能力,与 CLI 完全兼容、可互相收发,--qr 扫码即进接收页;Android 上有 crocgui、croc-app 等 F-Droid 客户端,桌面社区 GUI 也有好几个。不过完整能力仍在命令行。

采用顺序

  1. 先安装:brew install croc,或用对应平台的其他包管理器(scoop、choco、MacPorts、Nix、pacman、pkg、conda 等),也可以 curl https://getcroc.com | bash 直接装。
  2. 试传一个小文件,体验基本流程。
  3. 尝试传文件夹和大文件,测试断点续传。
  4. 正式使用前,最好自建一个中继(croc relay),把传输链路收进自己手里。
  5. 需要时配 --socks5 走 Tor 等代理提高匿名性。
  6. 双方无法同时在线的场景,试试 croc store 的加密暂存。

顺带一提:CLI 现在自带更新机制——每 24 小时最多一次后台检查新版本,传输结束后提示;croc update(别名 croc upgrade)手动升级,且不会覆盖包管理器安装的二进制。

总结

Croc 把一个复杂的问题——安全跨平台文件传输——封装成了极简的体验:发送方输入命令得到一串助记词,接收方输入这串助记词,文件就开始传了。PAKE 保证口令可以很短,直连优先保证速度,加密元信息和断点续传把细节兜住。

发文时它还只是"一个加密传文件的工具";三个月后 v11 把边界推到了加密暂存(croc store)、共享终端(croc ssh)和浏览器互转。方向没有变:凡是需要两端安全交换的东西,都收敛到同一行命令、同一个口令协议上。

如果你还在为"怎么把这个大文件传给另一台电脑"而烦恼,Croc 值得立即尝试——你唯一需要记住的,就是那串助记词。

参与讨论

使用 GitHub 登录。欢迎补充事实、异议与实践。