mirror of
https://github.com/qtmleap/revkit.git
synced 2026-09-24 20:11:50 +02:00
11 KiB
11 KiB
Netflix Android 起動時認証フロー
対象: Netflix Android v9.57.0 (build 63928) デバイス: Pixel 4a (5G) / bramble / Android 14 取得方法: Frida フック (
hook_netflix_android.js) +run.pyによる JSONL キャプチャ キャプチャ日: 2026-03-14
1. 概要
Netflix Android アプリの起動時に発生する認証・プロビジョニングフローを、キャプチャしたトラフィックから再構成した。
各 API のリクエスト・レスポンス詳細は auth_flow_android_api.md を参照。
1.1 通信先エンドポイント
| エンドポイント | 略称 | プロトコル | 用途 |
|---|---|---|---|
android14.appboot.netflix.com |
appboot | HTTPS (MSL レスポンス) | デバイス登録・nfvdid Cookie 発行 |
android14.prod.ftl.netflix.com |
prod.ftl | HTTPS + MSL | MSL API (/nq/androidui/samurai/) + GraphQL |
android14.prod.cloud.netflix.com |
prod.cloud | HTTPS + MSL | GraphQL via MSL |
1.2 通信の分類
| 分類 | Content-Encoding | ボディ形式 | 認証方式 |
|---|---|---|---|
| MSL API | msl_v1 |
CBOR → GZIP → JSON | MSL トークン (Master Token + User Auth Data) |
| Non-MSL GraphQL | なし | JSON | Cookie (NetflixId / SecureNetflixId) |
| Appboot | なし (レスポンスは application/x-msl+json) |
Query String | nfvdid Cookie |
2. 認証フロー全体像
%%{init: {'theme':'dark'}}%%
sequenceDiagram
participant App as Netflix App
participant AB as appboot
participant FTL as prod.ftl<br/>(MSL API)
participant Cloud as prod.cloud<br/>(GraphQL)
Note over App: 起動
App->>App: ProxyESN 期限チェック (※)
rect rgba(180, 100, 50, 0.2)
Note over App,Cloud: Phase 1: リクエスト送信
App->>AB: POST /appboot/{BaseESN}
App->>FTL: [MSL] /getProxyEsn
App->>FTL: [MSL] /aleProvision #1 (RSA鍵交換)
App->>Cloud: [MSL] RenewSSOToken
App->>FTL: CurrentCountryQuery
App->>Cloud: [MSL] InterstitialForProfileGate
App->>Cloud: [MSL] InterstitialForLolomo
end
rect rgba(50, 180, 80, 0.2)
Note over App,Cloud: Phase 2: レスポンス受信
FTL-->>App: CurrentCountryQuery → +NetflixId, SecureNetflixId [Cookie]
FTL-->>App: getProxyEsn → MSL レスポンス
FTL-->>App: aleProvision #1 → MSL レスポンス
Cloud-->>App: RenewSSOToken → 200 OK
AB-->>App: appboot → nfvdid [Cookie] + DeviceIdToken
Cloud-->>App: InterstitialForLolomo → 200 OK
Cloud-->>App: InterstitialForProfileGate → 200 OK
end
rect rgba(60, 120, 200, 0.2)
Note over App,Cloud: Phase 3: 追加リクエスト
App->>Cloud: [MSL] AccountQuery
Cloud-->>App: AccountQuery → 200 OK
App->>FTL: [MSL] /aleProvision #2 (RSA鍵交換)
FTL-->>App: aleProvision #2 → 新 PXA ESN 発行
App->>App: proxyEsn.onKnown (新ESN保存)
App->>FTL: PromoProfileGateVideoDataQuery
FTL-->>App: 200 OK + NetflixId, SecureNetflixId [Cookie]
end
rect rgba(150, 150, 150, 0.15)
Note over App,Cloud: Phase 4: 遅延リクエスト
App->>FTL: [MSL] FetchConfigData
App->>Cloud: [MSL] AccountQuery #2
FTL-->>App: 200 OK
Cloud-->>App: 200 OK
end
3. フェーズ概要
3.1 Phase 0: ProxyESN 期限チェック
アプリ起動時、ProxyEsn.$init でキャッシュ済み PXA ESN の期限がチェックされる。期限切れの場合はサーバーから新しい PXA ESN を取得する。
※ キャプチャ時の注意: 今回のキャプチャでは Frida フック (
hook_msl.js) がProxyEsn.$initでexpiredフラグを強制的にtrueに書き換えているため、TTL が残っていても必ず再取得が発生する。通常の動作では TTL が有効ならキャッシュがそのまま使用される。
3.2 Phase 1: リクエスト送信
起動後、以下のリクエストが並列に送信される:
- Appboot — デバイス登録・Cookie 発行
- getProxyEsn (MSL) — PXA ESN 取得
- aleProvision #1 (MSL) — RSA-OAEP-256 鍵交換
- RenewSSOToken (MSL → GraphQL) — SSO トークン更新
- CurrentCountryQuery — 国情報取得 + 認証 Cookie 発行
- InterstitialForProfileGate / InterstitialForLolomo (MSL → GraphQL) — UI 中間画面チェック
3.3 Phase 2: レスポンス受信
Phase 1 のレスポンスが順次到着する。主要なデータ:
- CurrentCountryQuery のレスポンスで
NetflixId/SecureNetflixIdCookie が発行される - appboot のレスポンスで
nfvdidCookie が更新され、x-netflix-deviceidtokenが発行される
3.4 Phase 3: 追加リクエスト
Phase 2 のレスポンスを受けて追加リクエストが送信される:
- AccountQuery (MSL → GraphQL) — アカウント情報取得
- aleProvision #2 (MSL) — 再鍵交換 → PXA ESN 確定
- PromoProfileGateVideoDataQuery — プロフィール画面データ取得
3.5 Phase 4: 遅延リクエスト
起動から約 30 秒後、バックグラウンドで送信される:
- FetchConfigData (MSL → Samurai) — デバイス・ストリーミング設定取得
- AccountQuery #2 (MSL → GraphQL) — アカウント情報再取得
4. 鍵交換と暗号化フロー
%%{init: {'theme':'dark'}}%%
sequenceDiagram
participant Client as Netflix App
participant Server as prod.ftl (MSL)
Note over Client: RSA-2048 鍵ペア生成
Client->>Server: aleProvision #1<br/>pubkey + scheme=A128GCM<br/>type=SOCKETROUTER
Server-->>Client: A128GCM セッション鍵<br/>(RSA-OAEP-256 で暗号化)
Note over Client: セッション鍵復号<br/>→ 以降の MSL 通信で使用
Client->>Server: aleProvision #2<br/>同一 pubkey
Server-->>Client: 新セッション鍵 + PXA ESN
Note over Client: PXA ESN 保存<br/>→ MSL ヘッダに使用
4.1 ALE Provision パラメータ
| パラメータ | 値 | 説明 |
|---|---|---|
keyx.scheme |
RSA-OAEP-256 |
鍵交換暗号化方式 (RSA with OAEP padding, SHA-256) |
keyx.data.pubkey |
Base64 RSA 2048-bit | クライアントが生成する使い捨て公開鍵 |
scheme |
A128GCM |
セッション暗号化方式 (AES-128-GCM) |
type |
SOCKETROUTER |
プロビジョニング種別 |
ver |
1 |
プロトコルバージョン |
4.2 aleProvision が 2 回呼ばれる理由
- #1: 起動直後に即座に鍵交換を開始。他のリクエストと並列。
- #2: getProxyEsn のレスポンスを受けた後に再度鍵交換。PXA ESN の発行と紐づく。
同一の RSA 公開鍵を使うが、サーバー側で新しいセッション鍵が発行される。
5. Cookie フロー
%%{init: {'theme':'dark'}}%%
sequenceDiagram
participant App as Netflix App
participant AB as appboot
participant FTL as prod.ftl
participant Cloud as prod.cloud
Note over App: 起動時: nfvdid (既存) のみ保持
App->>AB: POST /appboot (nfvdid Cookie なし)
App->>FTL: CurrentCountryQuery
App->>Cloud: [MSL] RenewSSOToken
FTL-->>App: set-cookie: NetflixId=..., SecureNetflixId=...
Note over App: ← 認証 Cookie 取得
AB-->>App: set-cookie: nfvdid=... (更新)<br/>x-netflix-deviceidtoken: ...
Note over App: ← デバイス Cookie 更新 + Device ID Token 取得
App->>FTL: PromoProfileGateVideoDataQuery
Note over App: NetflixId / SecureNetflixId Cookie を送信
FTL-->>App: set-cookie: NetflixId=..., SecureNetflixId=...
Note over App: ← 認証 Cookie リフレッシュ
5.1 Cookie 一覧
| Cookie 名 | 発行元 | 用途 | 発行タイミング |
|---|---|---|---|
nfvdid |
appboot | デバイス識別子 | appboot レスポンス (set-cookie) |
NetflixId |
prod.ftl | ユーザー認証 | CurrentCountryQuery レスポンス |
SecureNetflixId |
prod.ftl | セキュア認証 (HTTPS only) | CurrentCountryQuery レスポンス |
5.2 重要な観察
- MSL リクエストは Cookie に依存しない。 MSL は独自の Master Token + User Auth Data で認証を行う。
- Non-MSL GraphQL は Cookie で認証する。
NetflixId/SecureNetflixIdが必要。 nfvdidは全リクエストに付与されるが、認証には直接使われない (デバイストラッキング用)。
6. ProxyESN ライフサイクル
%%{init: {'theme':'dark'}}%%
stateDiagram-v2
[*] --> Cached: アプリ前回終了時に保存
Cached --> CheckTTL: 起動時に TTL チェック
CheckTTL --> Active: TTL 有効 → キャッシュ使用
CheckTTL --> Requesting: TTL 期限切れ → /getProxyEsn 送信
Requesting --> Provisioning: aleProvision #1 + #2
Provisioning --> Received: proxyEsn.response
Received --> Active: proxyEsn.onKnown<br/>(新 ESN キャッシュ保存)
Active --> [*]: MSL 通信で使用
6.1 ESN の変化
| タイミング | ESN |
|---|---|
| 起動時 (キャッシュ) | NFANDROID1-PXA-P-L3-GOOGLPIXEL=4A==5G=-22594-02025HJ6UHN1D... |
| 更新後 | NFANDROID1-PXA-P-L3-GOOGLPIXEL=4A==5G=-22594-0202DQTKQKE1UP... |
22594= Widevine System ID (固定)- 末尾の HMAC 部分のみが変化 (サーバー側で新規生成)
7. セキュリティ上の観察
- 二重認証メカニズム: MSL (Master Token ベース) と Cookie (NetflixId ベース) の 2 系統が並行して使われる。
- PXA ESN ローテーション: PXA ESN は TTL ベースで管理され、期限切れ時にサーバーから新しい ESN を取得する。今回のキャプチャでは Frida が強制失効させているため、毎回再取得が発生している。
- 鍵の使い捨て: aleProvision の RSA 鍵ペアはセッション単位で生成される (2048-bit)。
- Persisted Query: GraphQL クエリ本文をネットワーク上で送信しないため、API スキーマの隠蔽に寄与。
- Cookie の分離:
SecureNetflixIdは Secure 属性付きで HTTPS でのみ送信される。 - 並列リクエスト: 起動時間を最小化するため、認証に必要な全リクエストが並列に送信される。
- トークンの用途限定:
ssoTokenはRenewSSOTokenのみ、x-netflix-deviceidtokenは appboot レスポンスでのみ観測された。これらのトークンが HTTP レベルで他の API に転送される様子はなく、MSL レイヤー内部での使用またはローカル保持が示唆される。