Skip to content

Security: mirute02/tvremocon

Security

docs/security.md

セキュリティ — 何が守られていて、何が守られていないか

「他のアプリに乗っ取られてテレビを操作されないか」への回答。 結論から言うと、他アプリからの乗っ取りは塞いであるが、同じ Wi-Fi にいる人端末を手に取れる人は別の話で、そこは仕組み上どうにもならない部分がある。

1. 他のアプリがボタンを押せるか — 塞いである

経路 対策
ブロードキャストを投げて送信させる TvRemoteWidgetandroid:exported="false"。他アプリからは届かない
こちらの PendingIntent を横取りして書き換える どれも FLAG_IMMUTABLE。受け取った側がフィールドを埋められない
別ウィジェットのボタンを押させる PendingIntentrequestCode + action + data + component で同一判定され、extras は無視される。extras だけで区別すると 2 つのウィジェットが同じ PendingIntent を共有してしまうため、requestCode = appWidgetId * MAX_SLOTS + slottvremocon://widget/<id>/slot/<n> の data URI の両方で区別している
デバッグ画面を開かせて任意のキーを送らせる DebugActivityexported="false"。パスワード入力欄があり全キーを送れる画面なので、他アプリから表示できてはいけない
設定画面を任意の widgetId で開かせる WidgetSetupActivityAPPWIDGET_CONFIGURE のため exported にせざるを得ない。そこで起動時に getAppWidgetIds()自分のプロバイダの id か検証し、違えば即 finish() する

2. 資格情報の保護

  • 生パスワードは保存しない。 設定画面で authHash を導出したら破棄する
  • authHashパスワード相当(これがあればハブに認証できる)なので、平文保存はしない:
    • Android Keystore の鍵で AES-GCM 暗号化
    • noBackupFilesDir に保存(allowBackup=false だけでは一部メーカー端末の端末間移行を止められないため)
    • dataExtractionRules / backup_rules でクラウドバックアップと端末間移行の両方から除外
  • Keystore 鍵にユーザー認証を要求していない。ロック画面の上でウィジェットが動く必要があるため。 つまり端末のロックが破られた場合、authHash は取り出されうる
  • 保存した資格情報を破棄するのは、その鍵ではもう復号できないと分かる場合だけ (GCM タグ不一致、鍵の無効化・消失)。Keystore の一時的な失敗や読み取り失敗では消さない。 以前は例外を区別せず消していたため、一過性の不調で再設定が必要になりえた
  • authHash / パスワード / セッション Cookie はログに出さない(Cookie は有無だけ記録)

3. 通信

  • KLAP v2 は AES-128-CBC + SHA256 署名で、中身は暗号化されている。HTTP なのは外側の枠だけ
  • 応答の署名を検証している。 AES-CBC 自体は改ざんを検知しないので、 復号の前に SHA256(署名鍵 || seq || 暗号文) を再計算して定数時間で比較する。 一致しなければ「結果不明」として扱い、再試行しない(要求自体は送信済みで、 かつ妨害している相手にもう一度機会を与えないため)。署名の偽造には資格情報由来の鍵が必要
  • ただし network_security_config.xmlcleartext をアプリ全体で許可している。 domain-config はホスト名にしか効かず、ハブのアドレスは DHCP 次第で変わるため、XML では絞れない。 代わりに HubEndpoint がコードで絞っている:
    • プライベート IPv4 リテラルのみ(10/8, 172.16/12, 192.168/16, 169.254/16)
    • ポート 80 固定
    • パスは /app/{handshake1,handshake2,request} のみ
    • リダイレクトを追わないfollowRedirects(false)
  • 通信先は Wi-Fi の Network に固定。モバイル回線へ漏れない
  • LAN の自動探索は 10 分に 1 回まで。 探索はサブネットの全アドレスに TCP 接続し、 ポート 80 が開いている相手に handshake を投げるので、自宅以外のネットワークで 押下のたびに実行されると迷惑になる。設定画面の手動探索は制限しない

4. 塞げていないもの(仕組み上の限界)

  • 同じ LAN にいて TP-Link アカウントを知っている人は、このアプリと同じことができる。 これはハブ側の認証の話で、アプリでは変えられない。KLAP は LAN 内の誰でも ハンドシェイクを試せる(資格情報が合わなければそこで止まる)
  • 端末のロックを破られた場合、ウィジェットのボタンは押せるし authHash も取り出されうる。 ウィジェットがロック画面上で動く以上、これは設計上のトレードオフ
  • TP-Link の非公式 API を使っている。ファームウェア更新で挙動が変わりうる
  • ハブ自体の脆弱性はこのアプリの範囲外

5. 意図的にやっていないこと

  • 自動再送しない。 アプリ層・KLAP 層・HTTP 層のそれぞれで抑止している。 応答が失われた場合は「失敗」ではなく「結果不明」と表示する。 勝手に再送すると音量が 2 段階上がるなど、物理的な影響が二重になる
  • 期限切れの操作は捨てる。 連打で詰まった古いタップを後から実行しない
  • ブロードキャストを長く保持しない。 Android は同じ受信側へのブロードキャストを直列に配信するため、 待機のために保持すると後続のタップがその裏で滞留する。休止に戻す待機は保持せずに行う

There aren't any published security advisories