Introduction

相互 TLS の検証作業をしている時、Android 側の検証が上手くいかなくて、network-security-config によってカスタムの CA を読み込ませようと思っていた。
そうしたら、全然動かないから、本当に network-security-config の設定効いている?って思い、usesCleartextTraffic="false" にしたところ、何事もなく動いて「はぁ?」と思ったのが発端。

非 HTTPS の遮断について、各 OS の取り組みが下記

  • iOS
    • App Transport Security (ATS)
      • iOS 9.0 と macOS 10.11 以降、NSURLSession を使った全ての HTTP 通信に対して App Transport Security (ATS) を適用。
      • 2016年発表、2017年1月1日適用
  • Android
    • usesCleartextTraffic
      • Android 9.0 (Pie) からデフォルトで無効 (つまり平文の HTTP は許可しない)
      • デフォルト値は、API レベル 27 以前をターゲットとしているアプリの場合 true、API レベル 28 以降をターゲットとしているアプリの場合 false

この挙動に振り回されてので忘備録。

What happend?

結論は、使っている http 通信パッケージと flutter のバージョンの問題。

http 通信パッケージの問題

dart/Flutter では http 関係の通信パッケージは結構多くて、その中でも自分は

を使っている。

で、この二つは dart で実装された非ネイティブなコードになっており、これが原因。
というのも、ATS にしても usesCleartextTraffic にしても、これらが作動するのはネイティブ API を使った場合。
上の ATS の説明にもあるように 「NSURLSession を使った」、とあるようにネイティブ実装ではない、上記の 2 パッケージはこの制約を受けない。

試しに、ATS と usesCleartextTraffic を設定 (つまり非 HTTPS の強制遮断) し、非ネイティブ実装、ネイティブ実装 (といっても inter-op が考慮されており dart で扱える) で挙動を比べてみた。

下記のパッケージを比較している。

iOS Android

この通り、非ネイティブ実装は ATS や usesCleartextTraffic の影響を一切受けていない。

flutter のバージョンの問題

確認はしていないが、flutter の過去のバージョンで、ATS や usesCleartextTraffic のように、OS 側で強制的に非 HTTPS 通信を遮断していた時期があった模様。
それが、Insecure HTTP connections are disabled by default on iOS and Android

結局、このページ書かれているように Non-secure connection to local IP refused #72723 で議論され、この実装は元に戻された。
下の方には

  • Landed in version: 1.23
  • In stable release: 2.0.0
  • Reverted in version: 2.2.0 (proposed)

とあり、Flutter 2.2.0 以降は、この問題は発生しないと思われる。
2.2.0 のリリースが Windows 版で 2021/5/19 なので、その辺りに書かれたブログなどで、HTTP の遮断についての話をしているなら、それはもう古い記事である。
これらとかね。

少なくとも、http パッケージや dart:io パッケージなら、 Bad state: Insecure HTTP is not allowed by platform なんて表示されないのです。