CLOUSD
注册账户
开发者

通过互联网安全使用ADB

把adb从你的笔记本延伸到远处手机的四种方式、每种方式的命令,以及防止调试桥变成敞开大门的习惯。

adb原本是为USB数据线设计的,后来扩展到了局域网。它也能跨互联网工作,而且很多团队都依赖这一点:一个区域的CI运行器连接另一个区域的手机,开发者在家里连办公室的机架,测试套件驱动云手机。但它的默认设置假定网络是可信的。配置错了,调试桥就会变成任何发现它的人都能用的远程shell。

本指南讲清adb如何通信,四种通过互联网安全延伸adb的方式及完整命令,以及一份让它保持关闭的检查清单。

adb如何通信

涉及三个部分。adb客户端是你输入的命令。adb server是同一台机器上的后台进程,监听127.0.0.1:5037,负责跟踪设备。adbd是手机上的守护进程。server通过USB连接adbd,执行adb tcpip后通过明文TCP连接,在Android 11及以上开启无线调试时则通过TLS连接。

远程使用,就是把其中一段连接拉长到互联网上:客户端到server,或者server到adbd。拉长哪一段,决定了用哪种方法。

有两个事实决定了安全格局。adbd会要求用户为每台电脑的RSA密钥授权一次,这能挡住手机从未见过的陌生人。但经典的TCP模式什么都不加密:shell输出、logcat和你推送的每个文件都以明文传输。

绝对不要做的事

不要在路由器上把5555或5037端口转发到互联网,也不要在云防火墙里开放它们。2018年,一种名为ADB.Miner的蠕虫通过在5555端口暴露adb的安卓设备传播,主要是电视盒子,把它们变成了挖矿机。扫描器无时无刻不在寻找开放的adb端口。

也不要用-a启动adb server,它会让server监听所有网络接口。任何能连上这个端口的人都能控制主机上连接的所有设备,根本不会弹出密钥确认。

方式1:SSH隧道连到远程adb server

当手机通过USB连在一台你能SSH登录的主机上时用这种方式:办公室机架、实验室机器、CI服务器。把本地端口转发到主机的adb server,再让客户端指向它:

ssh -N -L 15037:127.0.0.1:5037 user@lab-host
adb -P 15037 devices
ADB_SERVER_SOCKET=tcp:127.0.0.1:15037 adb -s SERIAL logcat -d

本地端口不要用5037,以免你自己的adb server和隧道冲突。安装、推送、shell和logcat都能用,因为客户端通过它与server的连接传输一切。

有一点出乎意料:adb forward这时开的端口在实验室主机上,而不是你的笔记本上。想从自己的电脑访问转发的端口,就在SSH命令里为它再加一个-L。

SSH提供你本来就信任的密钥和加密,一条隧道就能覆盖一整层架子。代价是要运营一台主机、在上面开账号,而拥有这个账号的人能控制所有连着的手机。

方式2:SSH隧道直连adbd

当手机所在的网络只能通过跳板机访问、而你又无法通过USB接触那里的机器时,用这种方式。先通过USB或无线调试把手机切换到TCP模式一次,然后建隧道连过去:

adb tcpip 5555
ssh -N -L 15555:192.168.1.50:5555 user@jump-host
adb connect 127.0.0.1:15555

本地端口要选5555到5585范围之外的,因为adb server会把这个范围内的本地端口当作模拟器。手机必须保持TCP模式,而大多数系统重启后会退出这个模式;第一次连接时手机上仍会弹出密钥确认,所以需要有人去点一次接受。

方式3:经VPN的无线调试

Android 11加入了无线调试:先用配对码配对,然后建立TLS连接。它是为局域网设计的,但只要你的笔记本和手机能访问彼此的内网地址,经过WireGuard或Tailscale这类VPN也能用。在开发者选项里打开无线调试,选择使用配对码配对,然后运行:

adb pair 100.64.0.12:37099
adb connect 100.64.0.12:41235

adb pair会要求输入六位配对码。配对端口和连接端口不同,而且每次开关无线调试都会变。mDNS发现在大多数VPN上无法穿透,所以要手动输入手机上显示的地址。手机离开所在的Wi-Fi网络时,这个设置还会自动关闭,所以不适合任何无人值守的场景。

方式4:设备云中继

设备云在adbd前面放一个中继,给每台手机分配独立地址。细节因服务商而异。下面是Clousd中继的工作方式,与ADB文档中的说明一致:

  • ADB默认关闭。你按手机逐台开启,并设置最多20个IP地址或地址段的白名单。
  • 访问会自动过期:默认24小时后,最长7天。空闲会话30分钟后关闭。
  • 你用一次性验证码登录,验证码只显示一次。输错会关闭会话,多次失败会关闭中继。
  • root、remount、reverse、reboot等命令一律拒绝,每个会话都有日志。
adb connect HOST:PORT
adb -s HOST:PORT shell clousd-login CODE
adb -s HOST:PORT install app-debug.apk
adb -s HOST:PORT logcat -d

不用托管任何东西,也不用在自己的网络上开放任何端口。代价是少数命令不能用。依赖adb reverse的工作流,比如React Native应用访问你笔记本上的Metro server,需要另想办法,例如用一个手机能通过URL访问的开发服务器。如果你本来就没有可连的硬件,云端的远程安卓手机自带中继。

四种方式对比

SSH连adb serverSSH连adbd经VPN的无线调试设备云中继
手机在哪里通过USB连在你控制的主机上在跳板机后面的网络里在你的VPN能到达的网络里在服务商的数据中心
你要维护主机和SSH访问跳板机和TCP模式VPN、连着Wi-Fi的手机无需维护
访问控制SSH密钥SSH密钥加adb密钥VPN加配对白名单、过期时间、一次性验证码
手机重启后仍可用是通常不行不行是
adb reverse可用可用可用通常被拒绝
适合办公室机架、CI主机临时连一次实验室个人手机没有硬件的团队和CI

延迟和带宽

adb通信频繁,但对距离很宽容。往返200 ms时,交互式adb shell仍然可用,logcat也能流畅输出。安装和推送受限于你的上传速度,所以通过慢速的家庭上行链路传一个150 MB的APK要好几分钟。

插桩测试受影响最大,因为每个UI操作都要等回复。调高超时时间,把固定的sleep换成等待某个条件,而不是把sleep加长。如果通过SSH隧道用scrcpy投屏,用scrcpy --video-bit-rate=2M --max-size=1024降低码率和尺寸。在云手机上,这件事通常由浏览器画面来完成。

在CI中测试远程安卓设备

在远程手机上测试的CI任务有四步:连接、等待设备、运行、断开。如果你的中继需要登录,再加上登录这一步。

adb connect "$DEVICE"
adb -s "$DEVICE" wait-for-device
adb -s "$DEVICE" install -r app-debug.apk
adb -s "$DEVICE" install -r app-debug-androidTest.apk
adb -s "$DEVICE" shell am instrument -w com.example.app.test/androidx.test.runner.AndroidJUnitRunner
adb disconnect "$DEVICE"

设备地址和登录验证码要放在CI的密钥配置里,不要放进代码仓库。应用测试页面展示了同样的运行通过REST API是什么样子,在线测试安卓应用详细介绍了冒烟测试和logcat命令。如果你在考虑还要不要自己维护机架,见安卓真机测试平台。

安全检查清单

  • 永远不要把5037或5555端口暴露到互联网。
  • 把~/.android/adbkey当作凭据。它是你在每台信任过它的手机上的身份,不要复制到共用的机器上。
  • 对实验室里的手机,不时在开发者选项中“撤销USB调试授权”。
  • 优先选择会自动过期、只限已知地址的访问方式。
  • 用完后用adb disconnect和adb kill-server断开连接并停止server,并关闭无线调试。
  • 记录谁在什么时候连接了哪台手机。

继续阅读

全部指南

准备好启动
你的第一台云手机了吗?

免费试用30分钟

无需绑卡