root は、Android VPN アプリが通信を運び、漏えいを遮断し、再起動後も動作し続けるためのものではありません。人はこれを「アプリに VPN の許可がある」とまとめてしまいますが、その言い方には三つの別個の仕組みが隠れています。ユーザーによる同意、システムが確立するパケットインターフェース、そしてアプリが定義するものではないプラットフォームの遮断ポリシーです。Android VPN の設計を評価するとは、ある主張が実際にこの三つのどれに依拠しているかを問うことであり、それぞれに所有者も故障の仕方も異なります。

同意はシステムのアクティビティを通じて得られる

VpnService.prepare() は何も付与しません。同意がない場合、これは システムの同意アクティビティ に intent を返し、アプリはそのダイアログを起動してユーザーにサービスを承認させます。アプリ自身のプロセスを起動すること、マニフェストに権限を追加すること、独自の「VPN を有効にする」画面を表示することは、許可を作りません。

許可には範囲もあります。それはユーザープロファイル内のパッケージに属するため、個人用コピーと仕事用コピーは別々の判断であり、デバイスの所有者は管理対象プロファイルを個人用とは別に準備できます。ダイアログが一度も表示されず prepare() が null を返すなら、そのプロファイルのそのパッケージにはすでに同意があります。それはアプリが信頼できる証拠ではなく、許可が存在するというだけです。

インターフェースはシステムが満たす要求である

同意の後、アプリは VpnService.Builder で望む内容を記述します。アドレス、ルート、DNS サーバー、MTU、そして対応していれば含める・除外するアプリです。establish() はシステムにトンネルインターフェースを作成し、一致する通信をそこへ流すよう要求します。ルーティングの判断はシステムが持ちます。アプリはファイル記述子を受け取り、その後パケットに対して何か有用なことをしなければなりません。

What you observeWho actually owns it
VPN consent dialogThe system, triggered by prepare()
Routes, DNS and app listsThe app requests them; the system applies them
One active VPN per profileThe platform; a new service replaces the old one
Always-on and block-without-VPNDevice policy enforced by the platform

この表がアーキテクチャを一か所に示しています。アプリはパケット処理に関与しますが、インターフェース、ルーティングテーブル、実行できるサービスの数を所有するわけではありません。

トンネルのソケットは自分のトンネルから逃げなければならない

暗号化された通信を運ぶ接続自体がソケットです。それがアプリがたった今作ったインターフェースへ戻されると、トンネルがトンネルに依存して止まります。VpnService.protect() はそのソケットを下位のネットワークに結び付け、捕捉を迂回させます。だからこそ正しいクライアントは最初のハンドシェイクの前に外側のソケットを保護します。

adb shell dumpsys connectivity | grep -i -A5 vpn
adb shell settings get global always_on_vpn_app
adb shell settings get global always_on_vpn_lockdown

これらのコマンドは、インストールされた VPN ネットワーク、常時接続のパッケージ、ロックダウンが設定されているかを表示します。いずれも読み取り専用の観察です。値が設計の主張と一致しないなら、その主張はシステムの状態ではなくアプリの画面についてのものです。

遮断はプラットフォームの所属かもしれない

Android は VPN を使わない接続を遮断するポリシーを文書化しています。その強制はプラットフォームの契約であり、アプリがループで接続を再試行することではありません。スイッチは端末の VPN 設定に「VPN なしの接続をブロック」としてあり、管理対象端末では所有者がプロファイル全体に対して有効にできます。この区別はマーケティング上の主張を評価するときに重要です。クライアントは鍵アイコンを表示して積極的に再接続しながら、直接の通信は依然として可能かもしれません。あるいは対象アプリは、システムがロックダウンを強制しているためにオフラインのままかもしれません。

鍵アイコンはアプリが描くものなので、アプリが接続していると信じていることしか証明しません。別のアプリがネットワークアクセスを失うかどうかはプラットフォームが決めるため、有用な確認はトンネルが有効な間にそのアプリが失敗するのを見ることです。

アプリストアの「常時接続」ラベルもその契約の代わりにはなりません。常時接続とロックダウンは端末の設定であり、しばしば端末の所有者が適用し、除外されたアプリに対する挙動はプラットフォームが定義します。バッジではなく設定を読んでください。

設計レビューの四つの問い

第一に、誰が同意したのか。どのパッケージ、どのプロファイル、そしてユーザーか管理対象端末のポリシーか。第二に、アプリは何を要求したのか。どのルート、どのアドレスファミリー、どのアプリを含めたか除外したか。第三に、トンネルが使えないとき何が起きるのか。システムが遮断するのか、通信が下位のネットワークへ戻るのか。第四に、アプリは受け取ったパケットで何をするのか。

第四の問いは、許可モデルが答えられない問いです。同意と TUN インターフェースは、アプリが通信を受け取れることを証明しますが、暗号化するか、相手を認証するか、きれいに復旧するかは証明しません。それらは実装の性質であり、誠実なレビューはそこに時間を使います。

不要な root 要求は、結果をキルスイッチと呼んでも正当化されませんし、同意ダイアログはセキュリティ監査ではありません。三つの許可を分けて保てば、どの主張も検証可能になります。

関連記事