WiFiClientSecure ライブラリの SSL 証明書有効期限の検証等、セキュリティ問題を探ってみた

SSL ( TLS )

こんばんは。

今回は、ESP32 および M5Stack の DNS SSL サーバーを発表する予定で記事を作成していましたが、編集の収拾がつかなくなってきたので、記事を分けることにしました。
まず先に Arduino core for the ESP32 の WiFiClientSecure ライブラリを使う上で、SSL/TLS 証明書のセキュリティ上の問題点情報が上がってきたのでその紹介と、それを自分なりに検証してみたことについて述べたいと思います。

結論から先に言うと、現在の Arduino core for the ESP32 stable 1.0.0 の WiFiClientSecure ライブラリでは、以下の問題点があることがわかりました。

  • SSL 証明書 ( ルート CA , サーバー証明書 )の有効期限チェックは行われず、スルーしてしまう
  • 証明書の失効リスト ( CRL )の検証を行えない

これができないと、ESP32 や M5Stack でパスワード認証などで SSL の Web サイトに接続する時、悪意ある人が作った偽の SSL Webサイトに接続してしまっても、SSL接続しているから安心と思ってしまって、気付かれずにパスワードが盗まれる可能性があるということです。

昨今の世の中のセキュリティ意識の高まりと相まって、 Webサイトの殆どが SSL化 されています。
IoT 機器でも、ハックされる危険性が騒がれていて、セキュリティ熱が高まっていますね。
当ブログでも、ESP32 や M5Stack で Arduino core for the ESP32 (以下 Arduino – ESP32 と省略)を使って、Webサイトと WiFi で SSL 通信することが当たり前となっています。
その時、必要不可欠なのが、WiFiClientSecure ライブラリです。
これには、mbed マイコンでお馴染みの、mbedTLS ライブラリで構成されています。

そもそも、今回の記事を書く上で切っ掛けとなったことがありました。
Twitter のタイムラインで mbedTLS のツイートが目に入ってきたことが発端です。

私自身、見過ごせなかったので、ESP32 や M5Stack で DNS ネームの SSL サーバーを作って、実験してみました。
(これについては、次回の記事で紹介する予定です)
私自身、既存のライブラリに頼りきりになってしまって、SSL で暗号化されているから安心し切っていました。
でも、今回、実際に SSL サーバーを作って実験してみると、偽のSSL サーバーを作って、中間攻撃者のようなことをすることは、それほど難しくないなと思ってしまいました。

SSL 通信していて、ブラウザの URL 欄に鍵アイコンが出ているから安心だなんて、
とんでもねぇ!
と思う体質になりましたね。

では、これから紹介してみたいと思います。

この記事を作るにあたって、切っ掛けを与えて下さった mbed祭り プレゼンター SIProp 新里さんに感謝したいと思います。
ほんとうにありがとうございました。
m(_ _)m

因みに、私は独学のアマチュア素人です。
誤っていることや勘違いが有るかも知れません。
もし、何かありましたらコメント投稿欄等でご連絡いただけると助かります。

余談ですが、このブログの SSL 証明書の期限切れも迫っていたので、丁度良い機会で SSL ブランドを Scure Core から Digi Cert ブランドに変更しました。
これについても後で述べます。

スポンサーリンク

    【目次】

  1. mbed祭りで話題になった Arduino – ESP32 WiFiClientSecure ライブラリのセキュリティ問題点とは
  2. WiFiClientSecure および mbedTLS は DNS ネームの証明書でないと Verify ( 検証 )してくれない
  3. setCACert 関数で ルート CA 証明書をセットすれば、無効な証明書に差し替えた場合、検知してくれるのか?
  4. SSL 証明書で、有効期限チェックや失効リストの検証をしない場合の危険性
  5. WiFiClientSecure ライブラリの証明書日付時刻チェックを探る
  6. (余談)当ブログの SSL ブランドを変更しました
  7. 結論?

mbed祭りで話題になった Arduino – ESP32 WiFiClientSecure ライブラリのセキュリティ問題点とは

そもそも、今回の実験のキッカケは、2018/10/27 に Twitter のスイッチサイエンスさんアカウントから、

「mbedTLS の話題で盛り上がっていて、セキュリティなめているとマジでヤバイ!」

みたいなツイートが流れてきたんです。
そこには #mbed_fest というハッシュタグが付けられていました。
これは、mbed マイコンの mbed祭り というイベントのことです。

私は、mbed というマイコンは名前だけ知っていて、使ったことは無いのですが、私自身も、「mbed と言えば mbedTLS」と連想します。
なぜかと言うと、ESP32 や M5Stack でも使われている、Arduino core for the ESP32 (以下 Arduino – ESP32 )の SSL 通信で大活躍している WiFiClientSecure ライブラリは、mbedtls ライブラリで構成されているからです。
そして、Arduino – ESP32 ライブラリ群は ESP-IDF から作られているので、ESP-IDF の SSL 通信関連も mbedtls が使われています。

私自身も過去のこちらの記事で、ESP-IDF でコンパイルし直して、Arduino – ESP32 の WiFiClientSecure ライブラリのアセンブラファイルの設定を変えた実験をしたことがありました。
それに、過去のこちらこちらの記事のように、OpenSSL で SSL証明書を作成して ESP-IDF や Arduino – ESP32 で SSL サーバーを作ったり、Yahoo! Japan RSS サイトから SSL 通信でニュース記事を GET したり、Firebase Realtime Database と SSL 通信したりしていて、mbedtls は必要不可欠な存在になっているので、今回のツイートは無視できないものでした。

今回、 mbed_fest というものを初めて知りましたので、リンクを貼っておきます。

この mbed_fest ( mbed祭り ) は、とっても興味深い内容で開催されていたんですね。
IoT 機器開発の著作権のことやセキュリティのことなど、盛り沢山のようでした。
機会があったら是非行ってみたいと思いました。
その当日のスライド等も公開されていますので、以下のリンクを参考にしてみてください。

https://os.mbed.com/users/MACRUM/notebook/mbed-fest-2018-autumn-report/

これは、プログラムや電子工作をやっている方はかなり参考になると思います。

そこでかなり盛り上がっているのは、SIProp 新里 さんのプレゼンで、ESP32 の SSL 通信のことが話題に挙がっていて、ハッとさせられました。
なんと、

ESP32 および M5Stack Arduino – ESP32 で使われている SSL 通信 ( TLS 1.2 ) は、証明書の認証で日付時刻のチェックや証明書失効リストの検証をスルーしていて、セキュリティ的に問題がある。

ということでした。

そう言えば、Yahoo! Japan RSS サイトからニュース記事を取得する場合、setCACert 関数でルートCA をセットしなくても、問題無く SSL 通信ができてしまうことに疑問を持っていました。
私自身も今のところの ESP32 や M5Stack で通信している機密情報と言えば、Twitter API アカウント情報や Firebase アカウント情報でしょうか。
とすると、アカウントを乗っ取られる危険性を考えると、私にとっては無視できない問題です。
今まで Arduino – ESP のライブラリに全面的に頼りっ放しだったので、これはちゃんと調べねばならないと思いました。

そこで、自分で SSL サーバーを構築して実験することにしました。
レンタルサーバーを構築して証明書を作ることも考えましたが、いろいろな事情で敷居が高かったので、その方法はあきらめました。
ただ、ESP32 や M5Stack の SSL サーバーならば以前も作ったことがあるので、それで試そうと思いました。
OpenSSL で証明書を発行し、有効期限が切れた証明書も発行したりして、Arduino – ESP32 の WiFiClientSecure ライブラリに、セキュリティ上の問題点を探ろうと考えたわけです。
簡単に検証できるだろうと思っていました。

しかし!

そう甘くは無かった・・・。
理由は次で説明します。

WiFiClientSecure および mbedTLS は DNS ネームの証明書でないと Verify ( 検証 )してくれない

以前作った ESP32 の SSL サーバーは、証明書が ローカル IP アドレスで発行したものです。
IP アドレスベース SSL 証明書の SSL サーバーでは、スマホやパソコンからのブラウザからはアクセスできますが、別の ESP32 や M5Stack のclient から WiFiClientSecure ライブラリでアクセスしようとすると Verify が通らず、通信できませんでした。

原因は、WiFiClientSecure ライブラリ中の mbedTLS ライブラリが、IPアドレスベースの証明書の Verify を受け付けないことでした。
ネットでいろいろと検索して調べましたが、mbedTLS は DNS ネームベースでないと Verify してくれないとのことでした。
私の調べ方が間違っているのかもしれませんが、今のところそういう認識です。
これでは WiFiClientSecure の問題点を検証できません。

本来ならば、レンタルサーバーを借りるなりして、サーバー屋の DNS ホストネーム を使って SSL 証明書を入れ替えたりして検証する方法もありましたが、なかなか OpenSSL で作った自己証明書(通称:オレオレ証明書)は受け付けてくれません。

せっかく、Arduino – ESP32 のサンプルスケッチに DNSServer や mDNS があるので、これを何とか利用できないかと試行錯誤してみました。
かなり何度もトライアンドエラーを繰り返した結果、ついに、DNSServer ライブラリを使って、ESP32 および M5Stack のSSLサーバーが完成しました!!!
しかも、Android の Google Chrome や iOS の Safari などのブラウザに信頼される SSL サーバーです!

これによって、WiFi softAP モードで、インターネットを介さずに ESP32 および M5Stack の 同士で SSL 通信を行うことが出来るようになり、WiFiClientSecure の Verify も通りました。
ということは、mbedTLS や DNS ネームの証明書でしか Verify しないということもハッキリわかりました。

これで、ルート CA 証明書やサーバー証明書の有効期限を OpenSSL で自由に設定して、WiFiClientSecure の問題点を検証できる準備が整いました。
(M5Stack や ESP32 の DNS SSL サーバーの作り方については、次回の記事で取り上げる予定です。)

setCACert 関数で ルート CA 証明書をセットすれば、無効な証明書に差し替えた場合、検知してくれるのか?

ESP32 および M5Stack 同士の SSL 通信が可能になったので、まず試したのは、WiFiClientSecure ライブラリの中の
setCACert 関数
でルートCA 証明書をセットして、ペアでないサーバー証明書やルート CA 証明書にして client.connect する実験でした。

これは正しく、Verify ( 検証 )が通らず、通信できませんでした。
ちょっと安心しました。
setCACert 関数でルート CA 証明書をセットしておけば、「なりすまし」による偽サイトで偽のサーバー証明書にしてあれば、検知してくれて通信できないということになります。

が、しかし!!

これだけでは安全ではありません。
次で説明します。

SSL 証明書で、有効期限チェックや失効リストの検証をしない場合の危険性

では、SSL / TLS のルート CA 証明書や サーバー証明書の有効期限チェックや失効リスト ( CRL ) のチェックをしない場合、どんな危険性があるのか?

ルート CA 証明書は、有名なものならばブラウザには予めインストールされているので、そのまま使用してるとします。
インストールされてなくても、そのサイトを開けば、Windows 10 Google Chrome ならば鍵マークからインストールできます。
ESP32 や M5Stack でもそれを使ってコピーして、ルート CA はセットしてあるとします。

では、例として、Web サイトがハッキングされて、サーバー証明書や秘密鍵が盗まれたとします。
悪意のある者が、それで偽のコピーサーバーでフィッシングサイトを立ち上げます。
それにはハッキングした SSL サーバー証明書を使っていたとします。

そのサーバー証明書は先にセットしてあったルート CA 証明書とペアの鍵です。
ですから、偽サーバーには難無く SSL ハンドシェイクが行われて、SSL 通信できてしまいます。
ただ、悪意のある者は、ルートCA 証明書の秘密鍵を持っていないので、サーバー証明書の有効期限を更新することができません。
いずれ、年月が経つと有効期限切れになって、そのサーバー証明書は使えなくなるはずです。

しかし!

有効期限切れチェックをしない場合は、当然、SSL ハンドシェイクが行えて、Verify も通過してしまいます。
悪意のある者が、有効期限の切れたサーバー証明書と秘密鍵を盗んで偽サイトを運営するということも考えられます。
また、ルート認証局が、何らかの不都合な理由により、サーバー証明書の有効期限を変えて再発行していることも考えられます。
おそらく、有効期限チェックをしない場合の危険性はこんなところでしょうか。

また、OS に予めインストールされている ルート CA 証明書の有効期限も切れる可能性がありますね。
ブラウザの場合、おそらくチェックされていて、有効期限が切れたら何らかの警告が出ると思います。
私の素人レベルで考えられるのは、こんなところでしょうか。

また、ハッキングされたことが判明すれば、通常はルート認証局がサーバー証明書の失効処理をすると思います。
失効処理した場合は、失効リスト ( CRL )を発行し、URL リンクに貼られると思います。
ネットでザッと調べた情報では、ブラウザなどはこの失効リストを自動で常に検証しているらしいのです。
つまり、SSL サイトに接続すると、失効リストが掲載されているURL からダウンロードして、サーバー証明書と照合しているようです。

mbedTLS ライブラリにも、失効リストを検証する関数が用意されているようです。
ですが、Arduino – ESP32 の WiFiClientSecure にはありません。

いずれにしても、有効期限チェックや、失効リストチェックをすると、SSL サイトとのアクセスに時間がかかり、組み込みマイコンだと辛いものがありますね。
暗号化通信さえできれば、この作業を省いてしまうのも分からなくもありません。

WiFiClientSecure ライブラリの証明書日付時刻チェックを探る

では、Arduino – ESP32 の標準搭載ライブラリである、WiFiClientSecure ライブラリを再度ひも解いてみます。

Arduino – ESP32 stable版 ver 1.0.0 の場合、WiFiClientSecure ライブラリは、Windows 10 の場合、以下のフォルダにあります。
User-Name がご自分の PC のユーザー名です。

C:\Users\User-Name\AppData\Local\Arduino15\packages\esp32\hardware\esp32\1.0.0\libraries\WiFiClientSecure

その中の src フォルダに、以下の4つのファイルがあると思います。

WiFiClientSecure.h
WiFiClientSecure.cpp
ssl_client.h
ssl_client.cpp

基本的に、WiFiClientSecure.h をスケッチプログラム上に include すれば、Web と SSL通信できるプログラムが書けます。

Web サイトの host にアクセスする際に、connect 関数を使います。
この時、ssl_client.cpp 内にある、start_ssl_client 関数で Web サイトのルート CA 証明書と サーバー証明書で認証を行い、暗号化通信を行います。

start_ssl_client 関数内には、mbedTLS 関連関数がズラッと使われており、それらは主に、
libmbedtls.a
というアセンブラファイルに収められています。
このアセンブラファイルは、ESP-IDF でコンパイルされたものです。

関数のソースコードを見たい場合は、GitHub の 以下のリンクにあります。

https://github.com/espressif/esp-idf

その中の components フォルダに、mbedtls ライブラリがあり、それを開くと、 mbedtls は別のサブモジュールになっており、実体は以下のリンクにあります。

https://github.com/espressif/mbedtls

その中の、libraryフォルダ内に Cソースコードの mebedtls 関数群があります。

いかがでしょうか?

あまりに深すぎて、探索するのが嫌になります。
私は所詮アマチュア素人ですので、さすがに全部は読み解けませんでした。

分かる範囲で読み解いたところによると、setCACert 関数で ルート CA 証明書をセットする、つまり、start_ssl_client 関数内でルートCA証明書をセットしていれば、
MBEDTLS_SSL_VERIFY_REQUIRED
という定数でサーバー証明書と検証( Vefify )することになっています。
ルートCA証明書がセットされていなければ、
MBEDTLS_SSL_VERIFY_NONE
という定数で検証しないとなっています。

ところが!!!

ルートCA証明書をセットして、MBEDTLS_SSL_VERIFY_REQUIRED になっても、証明書の日付や時刻を検証してくれず、スルーしてしまいます。
これは、Twitter で先に紹介した SIPropの 新里 さんから教えて頂きました。

では、この定数や関数のソースを見ていきたいのですが、そこから先は全て libmbedtls.aファイルに収められてしまっているので、ソース ESP-IDF のものを見て探ることになります。
libmbedtls.aファイルは、ESP-IDF を使って、自分のプロジェクト内に Arduino components を組み込んでコンパイルすると生成できるファイルです。
これについては、以前、私が実験した記事に詳細が記載されているので、参考にしてみてください。

https://www.mgo-tec.com/blog-entry-arduino-esp32-wificlientsecure-hangup-solution.html

ESP-IDF の mbedTLS ライブラリ群を読み解いていくと、最終的に時刻や日付を検証しているのは、x509.c ファイルソース内の以下の関数に行き着きました。
(※私の解釈が間違えていたら、コメント投稿等でご連絡ください)

mbedtls_x509_time_is_past
mbedtls_x509_time_is_future

この関数は恐らく、証明書の日付時刻と、デバイスの現在時刻を検証して、問題無ければゼロを返すというもののようです。
ただし、
MBEDTLS_HAVE_TIME_DATE
というものが定義されている場合で、それが定義されていなければ、どんな日付でもゼロを返して、有効期限チェックがスルーされます。

そこで、config ファイルや、Arduino ソースで
#define MBEDTLS_HAVE_TIME_DATE
と定義してみて、NTP で時刻を取得して、期限切れの証明書をセットしてみました。

でも、どうしてもゼロが返ってきます。
mbedTLS のどれかの関数で時刻をセットしなければいけないんでしょうか?
独立させて mbedtls_x509_time_is_past や future 関数に値を代入してもダメでした。
やり方が間違えているかも知れません。

もう一つの手段として、ESP-IDF のプロジェクトに Arduino components を組み込んで、menuconfig で mbedTLS 関連を設定して、libmbedtls.a ファイルを再生成させる方法を試してみました。
そして、menuconfig 画面では、mbedTLS の下図のような設定項目があります。

ここに、

Enable mbedtls certificate expiry check

有効期限チェックを有効にするかどうかの設定ができるところがありました。
そして、mbedTLS の時刻を使うかどうかも設定できますね。
明らかに、このチェックをしてコンパイルして、config ファイルやら、libmbedtls.a ファイルを Arduino – ESP32 に使用すれば、証明書の有効期限チェックができそうな気がしました。

ただ、自分のやり方が悪いのか、どうしてもうまくいかず、ESP-IDF でコンパイルが通らず、Arduino – ESP32 でもコンパイルが通りませんでした。
もう、サッパリ分からず、完全にお手上げです。
Arduino – ESP32 で SSL 証明書の有効期限チェックはあきらめました。
恐らく、ESP-IDF で使用する場合は、有効期限チェックができると思われます。(試していませんが・・・)
私の場合は、Arduino – ESP32 で使うことを前提としているので、ESP-IDF の実験はしません(とっても面倒なので・・・)。

(余談)当ブログの SSL ブランドを変更しました

この記事を書く上で、タイミングよく当ブログの SSL 証明書の有効期限が切れそうなので、これを機に SSL ブランドを変更してみました。

Secure Core から RapidSSL というものに変更しました。

当ブログは Xserver を使っていますが、そこで扱っている独自 SSL ブランドで、Digi Cert という有名ブランドの割に格安というところと、証明書発行が素早いというところが選んだ理由です。
以前のブランドはサイトシール対応だったので、料金が高く、手放すことにしました。

ブランド変更時には数時間はサイトに接続できなくなることを予想して覚悟していたのですが、発行申請して、気が付いたらブランド変更も完了していて、あっという間でした。
RapidSSL の場合はサイトシールサービスは無いのですが、ロゴを貼ることができて、サイトシールっぽい感じでなかなか良いです。
Xserver は最近は Xアクセラレータ―などの機能が加わり、アクセスが速くなっていて、個人的にはおススメです。



ところで、この機にこのブログで SSL 証明書の有効期限を検証しようとしたのですが、このレンタルサーバーの場合はサーバー証明書を自由に扱うことができないので、mbedTLS の検証はできませんでした。
出来ない方が逆に安心ですけど。。。

結論?

と、いうことで、いろいろ試行錯誤しましたが、Arduino core for the ESP32 で SSL 証明書の有効期限チェックをできるような解決には至りませんでした。

最初にも述べましたが、もう一度、結論として述べておきます。

  • Arduino – ESP32 の WiFiClientSecure では、SSL 証明書 ( ルート CA , サーバー証明書 )の有効期限チェックは行われず、スルーしてしまう
  • 証明書の失効リスト ( CRL )の検証を行えない
  •  setCACert 関数でルート CA 証明書をセットした方が良い。
  •  SSL 通信で証明書があるから安心とは言えない。

以上をよく頭に入れた上で、WiFiClientSecure ライブラリを使うしかありません。
有効期限チェックや失効リストチェックを Arduino – ESP32 でやりたければ、残念ながら現時点では、自分で作るしかありません!

ということで、今回はここまでです。
次回は、ESP32 および M5Stack による DNS SSL サーバー構築方法を説明してみる予定です。

ではまた・・・。

Amazon.co.jp 当ブログのおすすめ

スイッチサイエンス ESPr Developer 32 Type-C SSCI-063647
スイッチサイエンス
¥2,420(2024/03/19 08:55時点)
ZEROPLUS ロジックアナライザ LAP-C(16032)
ZEROPLUS
¥22,504(2024/03/18 15:19時点)
Excelでわかるディープラーニング超入門
技術評論社
¥2,068(2024/03/19 03:23時点)

コメント

タイトルとURLをコピーしました