新しいビルドを出し、VPN に切り替えたら、同じ画面が読み込まれなくなりました。Android 9 以降では、より一般的な説明のほうが地味です。プラットフォームは API 28 以上を対象とするアプリに対して平文の HTTP を拒否し、VPN はそれが露見するだけの文脈を変えたにすぎません。表示されるエラーは HTTP クライアントによって異なるため、cleartext と明言されることはほとんどありません。

この失敗の正体

Android 9 以降、アプリが API レベル 28 以上を対象にした時点で、プラットフォームは既定で cleartext を遮断します。平文 HTTP のアドレスへのリクエストは、パケットが一つも端末を出る前にプラットフォームが拒否します。どのエラーが出るかは HTTP ライブラリ次第で、一般的なネットワーク障害のこともあれば、タイムアウトのこともあり、cleartext をはっきり名指すメッセージのこともあります。決して起きないのは、サーバーに届くことです。Android は既定の動作とドメイン単位の代替手段をセキュリティ設定ガイドで文書化しています。

同じ端末のブラウザーなら今でもページを読み込めます。これがほとんどの人が見落とす手がかりです。ブラウザーはアプリのネットワーク セキュリティ ポリシーの対象外です。

VPN が疑われる理由

たいてい三つのことが同時に変わります。VPN 接続後にアプリが別のホスト名を解決するようになった、端末が新しくビルドも新しい、あるいはビルド自体が既定を発動させる対象 API レベルへ移った、といった具合です。VPN は見えていて対象 SDK は見えないので、VPN が容疑者になります。

ネットワーク設定に触れる前に、旧ビルドと新ビルドの対象レベルを比べてください。27 以下を対象とするアプリは対象外で、「古い端末では動く」という報告はすべてこれで説明できます。

Expo のフラグが実際に効く場所

管理された Expo プロジェクトでは、app.json に設定を足しても Android マニフェストは編集されません。マニフェストを生成するのは prebuild と development ビルドだからです。その段階で設定を書き込むのが設定プラグインです。サポートされている方法は expo-build-properties で、アプリ設定の android の下に cleartext オプションを置いて構成します。

{
  "plugins": [
    ["expo-build-properties", {
      "android": { "usesCleartextTraffic": true }
    }]
  ]
}

このスニペットはプラグインのオプションを設定するだけで、マニフェストは後から生成されます。prebuild か新しい development クライアントのビルドを実行し、再テストの前に生成された AndroidManifest.xml に android:usesCleartextTraffic があるか確認してください。JS だけの再読み込みでは古いマニフェストが残るからです。プラグインのバージョンはプロジェクトが動く Expo SDK に合わせてください。プラグインのオプションはリリースごとに動くので、別の SDK からコピーしたスニペットは静かな無効化の確実な元です。

bare の流れとより狭い選択肢

bare React Native では、application 要素の android:usesCleartextTraffic、または指定したドメインにだけ cleartext を許可するネットワーク セキュリティ設定が相当します。選ぶべきはドメイン単位の形式です。内部 API を許可し、それ以外はすべて HTTPS のままにできるからです。例外リストも文書化されるので、レビュアーが尋ねるのはまさにその部分です。

どちらの方法も、名前を挙げていないものについてプラットフォームの既定を変えません。

まず端末ログで証明する

設定を編集する前に、正確な失敗を取ってください。接続した端末で adb logcat を実行すると、プラットフォームの cleartext 拒否が表示され、それを起こしたコンポーネント名も出ます。拒否されたソケット、解決できない名前、失敗した TLS ハンドシェイクはまったく別の場所を指しており、cleartext を有効にしても理由なくアプリを弱めるだけです。

そのうえで本番は誠実に保ちます。TLS を提供できないラボのエンドポイントには cleartext も許容されますが、予定できる最短の期間だけにしてください。API がプライベート ネットワーク内で TLS を終端できるなら、証明書を端末のトラスト ストアに入れ、cleartext は無効のままにします。トンネルが暗号化するのは出口までの経路だけで、その先ではありません。

エンドポイントが HTTPS で応答するようになったら、権限を外して再ビルドし、同じセッションで VPN と通常の Wi-Fi の両方でテストします。名前解決はこの二つの経路で異なることがあり、両方を比べて初めてどちらが正しいか分かります。

関連記事