# ViteConnect—— 扫码登录 ViteX,让你的资产更安全

# 背景

随着第三方网关入驻 ViteX (opens new window), ViteX 网页中越来越的的第三方服务也带来越来越多的安全风险。以往助记词存储在浏览器本地的方案日不足。迫切需要寻求一个远程签名方案,实现助记词与页面分离。通过手机 app 扫码登录的方案,用户成本低,不需要购买昂贵的硬件钱包。使用成本低,可以利用已经在手机中导入的助记词,不需要再在插件中再次导入助记词。且已经有很多开源的方案,开发成本较低。

考虑了两个相关方案:

  1. mewconnect (opens new window)
  2. walletconnect (opens new window)

两个方案的不同主要区别在于 peer 之间的连接方式。

# mewconnect

  1. mewconnect (opens new window) 实现了一个类似于 STUN server 的中间信号服务器。可以检测两个 peer 的 nat 类型,并向双方暴露对方的公网地址(某些 nat 类型下无法实现)。

  2. 后面就是两个 peer 之间的直接通讯。

提示

mewconnect 官网宣传中,与其它同类产品相比主要亮点是 P2P 通信可以实现无中间服务器,第三方无法访问通讯数据。这点可以通过在应用层建立加密层,对中间服务器透明来实现。(个人猜测 webrtc 由于大部分场景是针对音 / 视频数据,如果完全使用中间服务器,服务器压力较大,采用 p2p 通信的方式有一定优势)并且国内 3/4G 网络多处于对称型 NAT 环境中,难以实现 p2p 通信。

# walletconnect

walletconnect (opens new window) 实现了一个类似与 TURN server 的中继服务器(图中蓝色部分)

也是我们最终使用的方案。

# walletconnect 的握手过程:

1,web 端生成一个密钥 key,handshakeTopic,以二维码的形式展示出来,二维码同时包含服务端的地址。

2,web 端向服务器 的 handshake topic 发送一个会话请求(session request),包含 webclient topic,同时向服务器订阅 webcilent topic 的消息。

3,mobile 端扫码,连接到服务器,并从 handshaketopic 中取出 会话请求(session request),尝试用 key 解密消息,得到 webclienttopic,后续发送给 web 的消息讲通过这个 topic 发布。同时发送一个 approveSession 消息给 web,携带 mobileClientTopic 发送给 web 端。

4,web 端收到 approveSession,记录 mobileClientTopic,后续通过 mobileClientTopic 向 mobile 通道发送消息。

# walletconnect 特点:

整个过程的消息都是通过 key 加密的,对服务器透明 会话建立过程中通过 handshaketopic 交换了双方的私有 topic,该 topic 不对外透露。 一个会话过程中,session request 只发送一次,且只能被消费一次,保证了即使二维码中关于 handshakeTopic、key 等内容泄露,攻击者也无法获取到双方通讯的 topic。

# 一些问题:

key 在交换握手阶段没有更新并交换,如果二维码泄露,第三方获取到消息可以轻易解密。 peerTopic 只生成一次,缓存在本地(至少 web,ios 实现是这样),一旦泄露一次,就埋下安全隐患。 两个 peer 之间无法感知对方状态,服务器也无法感知两个 topic 的对应关系,可能是出于安全考虑尽量削弱 peer,服务器之间耦合。但是这样也给服务器层去做重连等功能带来了难题。 服务端和 web 端均以 socket.readyState===1 来判断消息是否可以发送。但是经过测试 (node),因网络问题断开连接后,socket.readyState 经过 5~7s 才会有变化。这个区间发送的消息有可能丢失。 总体来说 ,安全性考虑非常多,握手过程设计巧妙。但是工程实现中感觉非常匆忙,没有重连机制、服务端消息 topic 存储结构不太合理,且没有回收机制,各端的版本控制和兼容升级没有考虑。

# 我们的工作(ViteConnect 特性)

  1. 由于用户在国内外均有分布,国内网络环境复杂,一个中心化服务器延时高,稳定性差(多服务器正在计划中)。第一个问题就是要解决两个 peer 之间状态感知问题,不能让 mobile 由于网络原因断开后,web 端一直 pending 在消息发送的状态。ViteConnect 引入业务层心跳,10s 内没有检测到对方心跳,主动通知对方自己退出,同时自己断开。

  2. 实行一个 session 一个 topic,使 topic 回收成为可能。长时间(1h)未使用的 topic 将被回收。

  3. 服务端加入加入简单的监控,使之成为工程上可用的产品。

第一版发出后,随着 ViteX 上线,当日 ViteConnect 峰值 topic 数在 3700 左右,即大约有 1200 个用户同时在线。

# 第二次优化

第一版上线后有用户反馈,消息有时收不到,连接会因为手机锁屏而断开。稳定性和实时性是一对矛盾的要求,即在网络环境差的情况下是持续等待消息到达还是立即断开让用户感知,这是产品中的权衡。在考虑了 app 保活的难度,和网络延迟的影响后,我们计划实现重连机制。主要做了以下几方面的事情:

  1. 增加消息编号,为每条消息实现一个编号,peer 在重连后可以从上次断开的地方重新订阅,保障消息的到达,实现重连。

  2. 丰富更多的消息类型,让服务端可以实现更多功能。主要有以下种特点的消息:

    session 相关的消息出于安全考虑 只应被消费一次

    peer 与服务端之间的消息不应被 key 加密,可以利用服务器做一些连接优化。

    普通消息应在一定时间内被保留在服务端,以等待其他 peer 重连时重新订阅。topic 超过一定时间未被更新时被回收。

  3. 版本控制,消息、peer 的版本应该被服务器知道,服务器在对待不同版本的 peer、消息时兼容不同的处理方式。

参考资料:

https://developer.mozilla.org/en-US/docs/Web/API/WebRTC_API/Protocols (opens new window)

https://github.com/MyEtherWallet/MEWconnect (opens new window)

https://github.com/WalletConnect/walletconnect-monorepo (opens new window)

https://github.com/WalletConnect/node-walletconnect-bridge (opens new window)

https://www.jianshu.com/p/a6713687bc1d (opens new window)

https://www.jianshu.com/p/ecde412d2eeb (opens new window)