IoT Security 16 · 通信のセキュリティ

Chapter 16

通信のセキュリティ

この章がなぜ必要なのか——「TLS を使っています」は、何も保証しないから.

組み込み機器の TLS 実装で、証明書検証が無効化されている事例は、 驚くほど多い。理由はいつも同じである。

「つながらなかったので、検証を切ったら動いた。」

これで、中間者攻撃に対する防御が完全に失われる。 TLS は「使えば安全」ではなく、「正しく使えば安全」である。

この章で使う既出の用語(定義は各リンク先). リプレイ(06 章 6 節)、AES-CCM(08 章 6 節)、必要(09 章 6 節)、確実(14 章 6 節)、廃止(15 章 8 節)、強力(15 章 4 節)、運用(15 章 8 節)

1. 通信で守るもの

性質内容手段
機密性内容を盗聴されない暗号化
完全性改ざんされないMAC / AEAD
サーバ認証接続先が本物であるサーバ証明書の検証
クライアント認証接続元が正規の機器であるクライアント証明書(12 章)
前方秘匿性 (PFS)後から鍵が漏れても、過去の通信は復号されないECDHE などの鍵交換
リプレイ防止過去のメッセージを再送されないシーケンス番号、ノンス

「前方秘匿性」が組み込みでは特に重要である.

機器は 10〜20 年動く。その間に鍵が漏れる可能性は無視できない。

PFS がないと、攻撃者は通信を記録しておいて、 後で鍵を入手したときに全部復号できる(Harvest now, decrypt later)。

ECDHE を使う(RSA 鍵交換を使わない)ことで防げる。 TLS 1.3 では静的な RSA 鍵交換が廃止され、常に PFS がある。

2. TLS の正しい使い方

TLS で実際に守られるもの、守られないもの機器サーバClientHello証明書 + 鍵交換★ ここを検証しないと全部無意味証明書チェーン検証・ホスト名照合・有効期限確認クライアント証明書(相互認証の場合)暗号化された通信TLS が守るもの経路上の盗聴・改ざん・なりすまし(正しく検証していれば)TLS が守らないもの機器の中の鍵・サーバ側の実装・アプリケーション層の権限設計
TLS を「使った」ことと「正しく検証している」ことはまったく別。検証を切った瞬間に、ただの難読化になる

最低限のチェックリスト

#項目よくある失敗
1サーバ証明書を検証しているかVERIFY_NONE にしている
2ホスト名を検証しているか証明書は検証しているが、CN/SAN を見ていない
3有効期限を検証しているか時刻が合っていないので実質無効
4信頼するルート CA を限定しているか数百個のパブリック CA を全部信頼している
5TLS 1.2 以上か1.0 / 1.1 を許容している
6強い暗号スイートに限定しているかRC4、3DES、NULL 暗号が有効
7クライアント認証をしているかサーバが機器を認証していない
8失効を確認しているかCRL / OCSP を見ていない
9エラーを握り潰していないか検証失敗でも接続を継続している

失敗 1: 証明書検証の無効化

/* ★ 絶対にやってはいけない */
mbedtls_ssl_conf_authmode(&conf, MBEDTLS_SSL_VERIFY_NONE);

これで TLS は「盗聴されない通信路」ですらなくなる。 中間者が自分の証明書を提示すれば、機器はそれを受け入れて接続する。 以後、すべての通信が中間者に読まれ、改ざんされる。

/* ★ 正しい */
mbedtls_ssl_conf_authmode(&conf, MBEDTLS_SSL_VERIFY_REQUIRED);
mbedtls_ssl_conf_ca_chain(&conf, &cacert, NULL);
mbedtls_ssl_set_hostname(&ssl, "api.example.com");   /* ← ホスト名検証に必須 */

mbedtls_ssl_set_hostname() を呼び忘れると、ホスト名検証が行われない.

証明書チェーンは検証されるが、「その証明書が接続先のものか」は確認されない。 攻撃者が正規の CA から取得した別のドメインの証明書を提示すれば通ってしまう。

これは非常に多い実装ミスである。

失敗 2: 時刻が合っていない

証明書の有効期限を検証するには、正しい時刻が必要である。

証明書検証は時計に依存する機器の時計が 1970 年すべての証明書が「まだ有効期間前」機器の時計が 2099 年すべての証明書が「期限切れ」検証が失敗する「面倒だから検証を切ろう」 → 破滅
RTC がない機器・電池が切れた機器では日常的に起きる。時刻同期の設計を証明書検証とセットで考える
対策内容
RTC + バックアップ電池最も確実。だがコストと電池寿命
NTP / SNTP で同期だが NTP 自体が認証されていない(攻撃者が時刻を操作できる)
NTS(Network Time Security、RFC 8915)認証付きの時刻同期
TLS のハンドシェイクから時刻を得る卵と鶏の問題があるが、実用的な妥協
ビルド時刻を下限にする「ファームウェアのビルド日より前ではない」ことだけ確認する
サーバから安全に時刻を配る署名付きの時刻をアプリ層で受け取る

「ビルド時刻を下限にする」は実用的な妥協である.

/* 完全ではないが、何もないよりずっとよい */
if (current_time < BUILD_TIMESTAMP) {
    current_time = BUILD_TIMESTAMP;   /* 少なくとも過去の証明書は弾ける */
}

これで「有効期限の切れた古い証明書を使った中間者攻撃」は防げる。 完全な時刻検証の代わりにはならないが、検証を切るよりは遥かによい。

失敗 3: 信頼する CA が広すぎる

組み込み機器は、特定のサーバとしか通信しない。 なのにブラウザと同じ数百個のパブリック CA を信頼する必要はない。

/* 自社のサーバとしか通信しないなら、自社の CA だけを信頼する */
mbedtls_x509_crt_parse(&cacert, my_company_root_ca, sizeof(my_company_root_ca));
手法内容長所短所
プライベート CA のみ信頼自社の CA だけ攻撃面が最小CA の運用が必要
証明書ピンニング特定の証明書だけを受け入れる最強証明書更新時に文鎮化する危険
公開鍵ピンニング公開鍵をピン止め(証明書更新に強い)バランスがよい鍵の更新には対応が要る
パブリック CA を限定使う CA だけをバンドル運用が楽その CA が侵害されると危険

ピンニングは諸刃の剣である.

証明書を更新したら、全機器が接続できなくなる——という事故が実際に起きている。

安全な運用:

ピンニングを入れるなら、更新のシナリオを必ず設計すること。

3. リソース制約への対応

TLS は組み込みには重い。現実的な削り方を知っておく。

項目削減方法
RAM証明書チェーンのバッファ、レコードサイズ(MBEDTLS_SSL_MAX_CONTENT_LEN)を削る
フラッシュ使わない暗号スイート・機能をコンパイル時に除去
ハンドシェイクの時間セッション再開(TLS 1.3 の PSK / 0-RTT、TLS 1.2 のセッションチケット)
証明書のサイズECC 証明書を使う(RSA の 1/4 以下)
公開鍵演算ハードウェアアクセラレータ(08 章)

TLS 1.3 の利点

利点内容
1-RTT ハンドシェイクTLS 1.2 の 2-RTT より速い
0-RTT 再開再接続が非常に速い(ただしリプレイに注意)
暗号スイートの整理危険な選択肢が削除された。設定ミスが起きにくい
常に PFS静的 RSA 鍵交換が廃止された
ハンドシェイクの暗号化証明書も暗号化される(プライバシー向上)

0-RTT は便利だが、リプレイ攻撃を許す.

0-RTT で送ったデータは再送されうる(サーバ側で防ぐのが難しい)。 冪等な操作(GET など)にのみ使うこと。 「ドアを開ける」のような操作に使ってはいけない。

DTLS — UDP 上の TLS

用途内容
CoAP over DTLSCoAP(UDP 上で動く HTTP 相当の軽量プロトコル)と DTLS の標準的な組み合わせ
LwM2MDTLS を前提としている
リアルタイム性が要る通信TCP の再送遅延を避けられる

DTLS 1.3(RFC 9147)では、コネクション ID により IP アドレスが変わってもセッションを維持できる。 NAT の背後にいる機器や、モバイル機器で有用である。

さらに軽量な選択肢

方式内容
OSCORE(RFC 8613)CoAP のメッセージ単位で暗号化。プロキシを越えて End-to-End
EDHOC(RFC 9528)極めて軽量な鍵交換。OSCORE と組み合わせる
アプリ層の暗号独自プロトコル。強く非推奨だが、極小デバイスでは選択肢

独自プロトコルは避けること.

「TLS が重いから独自に作る」は、ほぼ確実に失敗する。 リプレイ防止、鍵更新、ダウングレード防止、エラー処理—— TLS が長年かけて解決してきた問題を、全部作り直すことになる。

どうしても軽くしたいなら、OSCORE + EDHOC のような 標準化された軽量プロトコルを検討すること。

4. 無線プロトコルのセキュリティ

BLE

項目内容
ペアリング方式Just Works(認証なし)/ Passkey / Numeric Comparison / OOB
LE Secure ConnectionsBLE 4.2 以降。ECDH による鍵交換。必須にすべき
レガシーペアリングBLE 4.0/4.1。中間者攻撃に弱い。無効にする
プライバシーResolvable Private Address で追跡を防ぐ

Just Works は「認証なし」である.

画面もボタンもない機器では Just Works になりがちだが、 ペアリング時に中間者がいたら防げない。

対策:

最後の手段が実務的には最も確実である。 BLE のリンク層セキュリティを信頼しきらず、アプリ層で認証する。

Wi-Fi

項目内容
WPA3SAE による鍵交換。辞書攻撃に強い。可能なら必須に
WPA2現在も広く使われる。KRACK 対策済みのスタックを使うこと
WPS無効にする。PIN 方式に既知の脆弱性がある
オープンネットワーク使わない。使うなら必ずアプリ層で暗号化
プロビジョニングSoftAP / BLE 経由で SSID とパスワードを渡す。この経路を暗号化する

Wi-Fi のプロビジョニング経路が盲点である.

「機器を AP モードにして、スマホから SSID とパスワードを送る」—— この通信が平文なら、近くにいる誰でも Wi-Fi パスワードを盗める。

その他

プロトコルセキュリティの要点
Zigbee / ThreadAES-CCM によるリンク層暗号。ネットワークキーの配布が要点
Matter証明書ベースの機器認証(DAC)、CASE/PASE。設計が最も現代的
LoRaWANAppKey / NwkKey による認証と暗号。鍵のプロビジョニングが要点
NB-IoT / LTE-MSIM による認証。アプリ層の暗号は別途必要
Modbus / CAN認証も暗号もない。上位で守るか、ネットワーク分離

産業プロトコル(Modbus、CAN、BACnet)には、そもそもセキュリティがない.

これらは「閉じたネットワークで使う」前提で設計されている。

IEC 62443 のゾーンとコンジットの考え方が、まさにこれである。

5. アプリケーション層

TLS の上でも、設計は必要である。

項目内容
認可認証(誰か)と認可(何ができるか)は別。機器ごとに権限を絞る
入力検証サーバから来るデータも検証する(サーバが侵害される可能性)
レート制限機器側でも異常な頻度の指令を弾く
コマンドの署名重要な操作(OTA、工場出荷リセット)は個別に署名する
リプレイ防止シーケンス番号やタイムスタンプ
ログ何が起きたかを記録する。改ざんされない形で

「クラウドは信頼できる」という前提を置かないこと.

クラウド側が侵害されたとき、 全機器に「工場出荷リセット」や「悪意あるファームウェア」を送れるなら、 被害は壊滅的になる。

対策:

6. MQTT の設定

MQTT は、ブローカ(中継サーバ)を介して「トピック」単位でメッセージを 出版/購読(Pub/Sub)する軽量プロトコルで、IoT で最も広く使われる。だから個別に触れる。

項目推奨
ポート8883(MQTT over TLS)。1883(平文)は使わない
クライアント認証X.509 クライアント証明書(X.509 は TLS で使う証明書の標準形式。12 章)。ユーザ名/パスワードより強い
クライアント ID証明書のサブジェクトと紐づける。任意の ID を名乗らせない
トピックの認可機器ごとに、読み書きできるトピックを限定する
ワイルドカード購読機器には許可しない(他機器のデータが見える)
Last Will切断検知に使える
MQTT のトピック設計 — 認証だけでなく認可も要る悪い設計全機器が同じユーザ名/パスワードsensors/# を全機器が購読できる1 台から認証情報が漏れたら全機器のデータが読まれる良い設計機器ごとの X.509 証明書devices/{deviceId}/up (書き込みのみ)devices/{deviceId}/down (読み取りのみ)deviceId は証明書のサブジェクトから導出他機器のトピックには触れない
認証(誰か)が通っても、認可(何をしてよいか)を絞らなければ被害は全体に広がる

7. 通信設定の検証

TLS 設定をチェックする

設定項目を切り替えて、どの攻撃が成立するかを確かめられる。

実機に対して確認する方法:

# サーバ側の設定確認
$ openssl s_client -connect api.example.com:8883 -showcerts
$ testssl.sh api.example.com:8883        # 包括的な診断

# 機器側の挙動確認(中間者を立てて、検証しているか確かめる)
# → 自己署名証明書を提示して、機器が接続を拒否するか確認する

「機器が不正な証明書を拒否するか」を必ず実機で確認すること.

コードレビューだけでは不十分である。 意図的に不正な証明書を提示して、接続が失敗することを試験項目に入れる。

これは出荷前試験の必須項目である。

8. この章のまとめ

ポイント内容
最大の失敗証明書検証の無効化。「つながらないから切った」
ホスト名検証set_hostname() を忘れると、別ドメインの正規証明書で通る
時刻合っていないと期限検証が機能しない。ビルド時刻を下限にするだけでも効く
信頼する CA自社の CA だけに限定する。パブリック CA を全部信頼しない
ピンニング強力だが証明書更新で文鎮化する。更新シナリオを必ず設計する
前方秘匿性ECDHE を使う。長寿命機器では特に重要
TLS 1.31-RTT、常に PFS、危険な選択肢の削除。0-RTT はリプレイに注意
軽量化ECC 証明書、セッション再開、OSCORE + EDHOC。独自プロトコルは避ける
BLEJust Works は認証なし。アプリ層で認証するのが確実
Wi-Fiプロビジョニング経路が盲点。DPP / 公開鍵暗号を使う
産業プロトコルModbus / CAN にはセキュリティがない。分離とゲートウェイで守る
MQTTX.509 クライアント認証 + 機器ごとのトピック認可。ワイルドカード購読を禁止
クラウドの前提クラウドも侵害されうる。破壊的操作には追加の署名を要求する
検証不正な証明書を提示して拒否されるかを実機で試験する

ここまでで一般論は終わりである。 次章から第 VI 部——各社の SoC が、これらをどう実装しているかを見る。