mirror of
https://github.com/qtmleap/revkit.git
synced 2026-10-19 00:01:52 +02:00
Netflix iOS/Androidアプリのリバースエンジニアリング結果をもとに、 通信仕様書および全技術ドキュメント(アーキテクチャ、MSL、ESN、認証、API、DRM、プロファイル、ヘッダー・Cookie、CDN、付録)を新規追加。 これにより、アプリ通信やDRM、認証フロー、ストリーミングプロファイル等の全体像と詳細な観測内容を体系的に参照可能となる。 あわせて、コミットメッセージAI生成設定・運用ルールも改善。 Git署名設定を一時的に無効化し、ドキュメント作成日時ルールを明文化。
9.5 KiB
9.5 KiB
5. API エンドポイント
5.1 appboot
デバイス登録と初期設定を行う。
%%{init: {'theme':'dark'}}%%
sequenceDiagram
participant App as Netflix App
participant AB as appboot.netflix.com
App->>AB: POST /appboot/{BaseESN}?keyVersion=1&suspended=false
AB-->>App: x-netflix-deviceidtoken (324B)<br/>set-cookie: nfvdid=...<br/>body: application/x-msl+json
リクエスト:
POST /appboot/{BaseESN}?keyVersion=1&suspended=false
Host: android14.appboot.netflix.com
レスポンス:
x-netflix-deviceidtoken(324 バイト) ヘッダーで DeviceIdToken を発行set-cookie: nfvdid=...で デバイス Cookie を更新- ボディは
application/x-msl+json形式
deviceidtoken および ssoToken は HTTP レベルで他の API に転送される様子は観測されず、MSL レイヤー内部での使用またはローカル保持が示唆される。
5.2 getProxyEsn (MSL)
PXA ESN を取得する。詳細は 3.5 PXA ESN 取得プロトコル を参照。
5.3 aleProvision (MSL) — Android 固有
ALE セッション鍵を確立する。詳細は 2.5 ALE を参照。
リクエスト:
{
"url": "/aleProvision",
"params": {
"provisionRequest": {
"keyx": {
"scheme": "RSA-OAEP-256",
"data": {"pubkey": "<RSA-2048 Base64>"}
},
"scheme": "A128GCM",
"type": "SOCKETROUTER",
"ver": 1
},
"netflixClientPlatform": "androidNative",
"appVer": "63928",
"mId": "GOOGLPIXEL=4A==5G=S",
"ffbc": "phone"
}
}
5.4 GraphQL (Persisted Query)
Netflix は GraphQL Persisted Query を使用し、クエリ本文をネットワーク上で送信しない。これにより API スキーマの隠蔽に寄与していると推定される。
%%{init: {'theme':'dark'}}%%
graph TB
subgraph MSL_GQL["MSL GraphQL (prod.cloud)"]
direction TB
SSO["RenewSSOToken<br/>a4d00303-..."]
PG["InterstitialForProfileGate<br/>18f3ae27-..."]
LO["InterstitialForLolomo<br/>3658ef5b-..."]
AQ["AccountQuery<br/>4043dd89-..."]
PB["InterstitialForPlayback<br/>07d88886-..."]
end
subgraph NonMSL["Non-MSL GraphQL (prod.ftl)"]
direction TB
CC["CurrentCountryQuery"]
PP["PromoProfileGateVideoDataQuery"]
end
MSL_GQL -->|認証: MasterToken + UIT<br/>暗号化: MSL| Cloud["prod.cloud"]
NonMSL -->|認証: Cookie<br/>暗号化: なし| FTL["prod.ftl"]
style MSL_GQL fill:#0f3460,stroke:#16213e
style NonMSL fill:#533483,stroke:#16213e
観測された GraphQL オペレーション:
| オペレーション | Persisted Query ID | バージョン | プロトコル | エンドポイント |
|---|---|---|---|---|
RenewSSOToken |
a4d00303-b02d-47c9-a53f-776b6a63b001 |
102 | MSL | prod.cloud |
InterstitialForProfileGate |
18f3ae27-a0f1-45c9-88e6-c6bd39159ecb |
102 | MSL | prod.cloud |
InterstitialForLolomo |
3658ef5b-0c6d-4c7d-a5c0-6ae11405ee1d |
102 | MSL | prod.cloud |
AccountQuery |
4043dd89-0ed5-4d7f-ac5c-40c7ffcec7ae |
102 | MSL | prod.cloud |
InterstitialForPlayback |
07d88886-1ec5-4115-98f2-7b6a20dbeae6 |
102 | MSL | prod.cloud |
CurrentCountryQuery |
未キャプチャ | — | Non-MSL | prod.ftl |
PromoProfileGateVideoDataQuery |
未キャプチャ | — | Non-MSL | prod.ftl |
MSL GraphQL vs Non-MSL GraphQL:
| 項目 | MSL GraphQL | Non-MSL GraphQL |
|---|---|---|
| エンドポイント | prod.cloud/graphql | prod.ftl/graphql |
| 暗号化 | MSL (CBOR → GZIP → JSON) | なし (平文 JSON) |
| 認証 | MasterToken + UserIdToken | NetflixId / SecureNetflixId Cookie |
| Cookie | nfvdid + flwssn + gsid | nfvdid + flwssn + NetflixId + SecureNetflixId |
5.5 Manifest / licensedManifest
ストリーミングマニフェスト (コーデック・品質プロファイル、CDN URL、DRM 情報) を取得する。
プラットフォーム比較
%%{init: {'theme':'dark'}}%%
graph TB
subgraph iOS_Flow["iOS: 分離型"]
direction TB
IOS_M["/manifest<br/>PRE_FETCH → standard"]
IOS_L["/license<br/>DRM 鍵取得"]
IOS_M --> IOS_L
end
subgraph Android_Flow["Android: 統合型"]
direction TB
AND_LM["/licensedManifest<br/>マニフェスト + ライセンス統合<br/>バッチ viewableId 対応"]
end
style iOS_Flow fill:#1a1a4e,stroke:#4a4ae0
style Android_Flow fill:#1a472a,stroke:#2d6a4f
iOS: /manifest + /license (分離型)
iOS ではマニフェスト取得とライセンス取得が別リクエスト:
PRE_FETCHフェーズ: UI 表示時にプリフェッチstandardフェーズ: 再生開始時/license: DRM 鍵取得
iOS リクエストパラメータ:
{
"viewableId": 81774276,
"flavor": "PRE_FETCH",
"profiles": ["h264hpl30-dash", "hevc-main10-L40-dash-cenc-prk"],
"profileGroups": ["live", "ce3", "ce4"],
"drmType": "fairplay",
"manifestVersion": "v2",
"desiredVmaf": "phone_plus_lts",
"hardware": "IPHONE9-1",
"osName": "iOS",
"osVersion": "15.8.3",
"platform": "2012.4"
}
Android: /licensedManifest (統合型)
Android ではマニフェストとライセンスが単一リクエストに統合されている。さらに複数の viewableId をバッチ送信可能。
Android リクエストパラメータ:
{
"version": 2,
"url": "/licensedManifest",
"languages": ["en-JP"],
"common": {
"challenge": "<Widevine CDM protobuf (共通チャレンジ)>"
},
"params": [
{
"viewableId": "81756595",
"profiles": ["playready-h264hpl30-dash", "vp9-profile0-L30-dash-cenc", "..."],
"profileGroups": [{"name": "primary", "profiles": ["..."]}],
"challenges": {
"primary": [{
"challengeBase64": "<Widevine CDM protobuf>",
"drmSessionId": 1,
"clientTime": 1773373148
}]
},
"method": "licensedManifest",
"flavor": "PRE_FETCH",
"drmType": "widevine",
"licenseType": "limited",
"manifestVersion": "v2"
}
]
}
Android レスポンス (L3 環境で完全復元, 456KB):
{
"id": 1,
"version": 2,
"serverTime": 1773373149628,
"result": [{
"movieId": "81756595",
"packageId": "2596051",
"duration": 8523000,
"drmContextId": "2596051",
"playbackContextId": "E3-Bgj5tevc...",
"video_tracks": [{
"trackType": "PRIMARY",
"new_track_id": "V:2:1;2;;primary;-1;none;-1;",
"streams": [{
"content_profile": "playready-h264hpl30-dash",
"bitrate": 1050,
"peakBitrate": 2250,
"res_w": 960,
"res_h": 540,
"framerate_value": 24000,
"framerate_scale": 1001,
"downloadable_id": "1496730611",
"vmaf": 87,
"isDrm": true,
"urls": [{"cdn_id": 140368, "url": "https://ipv4-c062-osa001-ix.1.oca.nflxvideo.net/?o=..."}],
"moov": {"offset": 108, "size": 1048},
"sidx": {"offset": 1156, "size": 37036}
}]
}],
"audio_tracks": ["..."],
"timedtexttracks": ["..."],
"servers": [{
"id": 140368,
"name": "c062.osa001.ix.nflxvideo.net",
"type": "OPEN_CONNECT_APPLIANCE",
"rank": 1,
"dns": {"host": "ipv4-c062-osa001-ix.1.oca.nflxvideo.net", "ipv4": "45.57.82.139"}
}],
"links": {
"events": {"href": "/events?playbackContextId=...&esn=..."},
"ldl": {"href": "/license?licenseType=limited&playbackContextId=..."},
"license": {"href": "/license?licenseType=standard&playbackContextId=..."}
}
}]
}
5.6 /license
DRM ライセンスを取得する。
%%{init: {'theme':'dark'}}%%
sequenceDiagram
participant App as Netflix App
participant API as MSL API
App->>API: /licensedManifest (PRE_FETCH)<br/>licenseType=limited
API-->>App: マニフェスト + limited ライセンス (~60秒)
Note over App: 再生開始
App->>API: /license?licenseType=standard
API-->>App: standard ライセンス (iOS: ~12h / Android: 可変)
Note over App: ビットレート切替時
App->>API: /license?licenseType=limited
API-->>App: limited ライセンス (~60秒)
Note over App: 再生終了
App->>API: /releaseLicense
API-->>App: 200 OK
Android (Widevine):
/nq/androidui/samurai/~9.0.0/api
?licenseType=standard
&playbackContextId=<JWT>
&esn=NFANDROID1-PRV-P-L3-...
&drmContextId=2596051
&challengeBase64=<Widevine protobuf>
&xid=<トレース ID>
iOS (FairPlay):
/nq/iosplatform/pbo_license/~1.0.0/router
?licenseType=standard
&playbackContextId=<Base64url セッション ID>
&esn=NFAPPL-02-IPHONE9=1-...
&drmContextId=<数値>
| 種別 | 用途 | 有効期限 |
|---|---|---|
standard |
通常再生 | iOS: 約 12 時間、Android: 可変 |
limited |
ビットレート切替・プリフェッチ | 約 60 秒 |
release |
再生終了通知 | — |
5.7 FetchConfigData (Samurai Config)
デバイス・ストリーミング設定を取得する。起動後約 30 秒の遅延リクエスト。
{
"method": "get",
"path": ["deviceConfig", "hendrixConfig", "networkScoreConfig", "accountConfig"],
"appType": "samurai"
}
5.8 /logblob
テレメトリデータを送信する。Android では android14.logs.netflix.com に MSL 暗号化で送信される。