近幾年移動端知識付費App,越來越多越來越火爆。例如:網易公開課、網易雲課堂、樊登讀書、邏輯思維、i春秋,甚至於知乎App都上架付費課程了。
移動端付費課程,基本是以視頻+音頻
形式呈現給大家的。那么對於這些付費課程:如何在保證用戶體驗的前提下,防止媒體資源被盜播,維護內容生產者的利益,就成為一個急需解決的問題
。
注意:保證用戶體驗是前提。否則再好的課,用戶體驗垃圾,也賣不出去。
如今市面上,移動端加密、防盜播的方式很多。這里只是討論一種:我認為的用戶體驗較好,技術實現成熟,又有效防盜播的方式
。
注意:防止盜播,並不能100%杜絕盜播。只能不斷增加App的破解成本,完全無法破解的App是不存在的。所以,想100%防止盜播也是不可能實現的。
一、實現方案
這里采用的方案是:客戶端播放AES-128加密的m3u8媒體資源
為什么是m3u8 ?
- m3u8采用AES-128對稱加密算法加密,技術成熟穩定
- 前邊說了,保證用戶體驗為前提
音視頻播放過程中,用戶進入播放頁后,音視頻的秒開率
(1秒內成功加載的播放數/播放總數)是影響用戶體驗的重要指標;m3u8媒體資源是一個文本文件,其由一個個ts視頻片段的播放地址構成,選擇合適的ts切片大小,能有效提高音視頻的秒開率,保證用戶的觀看體驗。
用戶體驗的大前提滿足了,那如何實現呢?
- 客戶端播放的視頻源為
AES-128對稱加密的 m3u8
媒體資源;
播放地址最終組織形式如下:
https://domain/course101.m3u8?playKey=i_am_decrypting_key101
- 播放地址
https://domain/course101.m3u8
與playKey
需從服務端獲取;
加密m3u8的解密關鍵就在於playKey
,因此,防止playKey被破解是防盜播的一個關鍵點
- 將最終播放地址交於播放器播放
二、實現舉例
對於m3u8媒體資源,無論是 視頻切片加密
、ts視頻片段解密播放
在技術實現上已經非常成熟,不存在技術壁壘,實現已經不是問題。
那么擺在移動端最主要的問題就是:防止媒體資源被盜播,維護內容生產者的利益
注:雖然技術實現已經不是問題,但這里還是會把 移動端技術實現的細節
和 防破解(防盜播)
的實現細節進行呈現。
技術實現上,大概分為以下幾個步驟:
- 獲取媒體資源播放地址
- 獲取媒體資源的解密token (playKey)
- 拼接最終播放地址,播放m3u8媒體資源
下面我們從移動端請求課程內容列表,到成功播放的全過程來舉一個例子:
1、獲取ID為101課程的內容列表
首先通過課程ID 101向服務端發起請求,獲取該課程的內容列表。請求方式和返回結果如下:
a、發起請求:
https://domain/xxx/getCourseList.do?courseId=101
為了簡單易懂,這里簡單模擬一個get請求:
- 請求參數
courseId
為101
; - 請求地址為
https://domain/xxx/getCourseList.do
; - 獲取id為101的課程,其對應的內容列表。
b、返回結果:
課程101的課程內容列表如下:
[
{
"mediaId": "101", //媒體資源id
"mediaUrl": "n3GuNo5Rc44anmLGrRU8Rne/JU9cHzc1vXZWiYEwcD0=", // 媒體資源的m3u8播放地址
"encryptId": "LLVahEH+HjZEOJk6RfJtww==" // 獲取媒體資源playKey的encryptId
},
{
"mediaId": "102",
"mediaUrl": "n3GuNo5Rc44anmLGrRU8RlKQGcM3X5R3oVqpuCrTpDk=",
"encryptId": "Ez3wHHWx5ddq+D2miV2dFg=="
},
{
"mediaId": "103",
"mediaUrl": "n3GuNo5Rc44anmLGrRU8RqLaIqPDWyZQ5VotuUSyNI8=",
"encryptId": "R5a31ZJIy9Z5+tNPplxHLQ=="
}
]
正如注釋中寫的:
mediaId
是媒體資源id
;mediaUrl
是媒體資源的m3u8播放地址
;
可能會疑問: n3GuNo5Rc44anmLGrRU8Rne/JU9cHzc1vXZWiYEwcD0= 是什么鬼,為什么不像播放地址?
這里正是防止媒體資源被盜播,維護內容生產者的利益的 第二步
這里將資源地址https://domain/course101.m3u8
進行了一個簡單的AES加密
對於其資源地址的加解密,會在下文進行詳細說明。encryptId
則是用於獲取媒體資源的解密playKey
。
encryptId的具體作用,會在下文中詳細說明
注:這里服務器要做一個用戶權限的校驗:未購買用戶不返回其 mediaUrl 和 encryptId
c、未購買用戶的返回結果:
未購買用戶不返回其 mediaUrl 和 encryptId
正是預防移動端被不良用心的人員,惡意采用Http抓包進行破解。是防止媒體資源被盜播,維護內容生產者的利益的 第一步
,也是很重要的一步。 因為:
- 移動端無論如何防破解,總不能100%保證無法破解;
- 想要獲取播放地址,必須購買課程,對於用心不了的人員,增加了其破解成本;
未購買用戶,通過課程ID 101向服務端發起請求,返回數據如下:
[
{
"mediaId": "101",
"mediaUrl": "",
"encryptId": ""
},
{
"mediaId": "102",
"mediaUrl": "",
"encryptId": ""
},
{
"mediaId": "103",
"mediaUrl": "",
"encryptId": ""
}
]
注:未購買用戶,請求某課程列表接口時,不要返回 媒體資源的m3u8播放地址
和 獲取媒體資源playKey的encryptId
。這很重要
d、mediaUrl與encryptId解密:
為防止移動端App被Http抓包,mediaUrl
與 encryptId
都不是明文傳輸的。因此移動端拿到課程的內容列表后,首先需要對mediaUrl
與 encryptId
進行解密。
關於解密:
- 對稱加密算法,大家可以根據實際需求,自行進行選擇;
- 解密相關代碼,
建議由C語言實現,打成SO添加到移動客戶端中
;
原因是增加移動端破解難道,提高破解成本
。
這里只是采用了簡單的AES對稱加密:
在這里插入圖片描述
完成解密后的數據格式如下:
[
{
"mediaId": "101", //媒體資源id
"mediaUrl": "https://domain/course101.m3u8", // 媒體資源的m3u8播放地址
"encryptId": "qazwsx101" // 獲取媒體資源playKey的encryptId
},
{
"mediaId": "102",
"mediaUrl": "https://domain/course102.m3u8",
"encryptId": "qazwsx102"
},
{
"mediaId": "103",
"mediaUrl": "https://domain/course103.m3u8",
"encryptId": "qazwsx103"
}
]
2、獲取 playKey
在第1步中,已成功獲取到媒體資源的播放地址 https://domain/course101.m3u8
與encryptId
。為了播放媒體資源,下面來獲取到加密m3u8視頻的解密playKey
這里 playKey
仍然需要從服務端進行獲取,獲取方式如下:
模擬請求
發起請求:
https://domain/xxx/getMediaToken.do?encryptId=C0AFC0A81&mediaId=101
返回結果:
{
"playKey": "I_am_playkey_001"
}
這里仍然模擬一個簡單get請求:
- 請求參數
encryptId
為qazwsx101
; - 請求參數
mediaId
為101
; - 請求地址為
https://domain/xxx/getMediaToken.do
;
注:playKey是加密m3u8解密的關鍵,是防止媒體資源被破解的重中之重。
關於 playKey 加密
這里是防止媒體資源被盜播,維護內容生產者的利益的 第三步和第四步
- 1、
playKey
服務端需加密傳輸
; - 2、請求
getMediaToken.do
接口時,服務端需對該用戶的購買狀態進行驗證,未購用戶不返回其對應的playKey; - 3、移動端獲取到playKey后,在SO(C層代碼達成的)中對playKey進行解密;
- 4、解密后的playKey存在一定的過期時間;或者使用次數限制(建議服務端限定只能使用一次)
加密算法要與mediaUrl 和encryptId 的解密算法區分開,增加破解成本
3、拼接播放地址
經過以上步驟,購買用戶成功獲取到 播放地址 https://domain/xxx/getMediaToken.do
和解密key I_am_playkey_001
。
將mediaUrl 與playKey 拼接起來,可以交給播放器播放了:
https://domain/course101.m3u8?playKey=i_am_decrypting_key101
到此,移動端的工作完成。
OK. 移動端完事大吉
。
到這里播放器與服務端的交互剛剛開始。
這里有必要先介紹一下,加密m3u8視頻的文件格式:
加密m3u8視頻的文件格式
#EXTM3U
#EXT-X-VERSION:3
#EXT-X-TARGETDURATION:19
#EXT-X-MEDIA-SEQUENCE:0
#EXT-X-KEY:METHOD=AES-128,URI="http://domain//hls.key"
#EXT-CUSTOM-YUNXIN:ntsversion=0,ntsprivatedata=d68edb4f4ee3c64233b1ece785758291727d053d
#EXTINF:11.960000,
http://domain/encrypt0.ts
#EXTINF:9.200000,
http://domain/encrypt1.ts
#EXTINF:10.000000,
http://domain/encrypt2.ts
#EXT-X-ENDLIST
以上為加密m3u8視頻的文件格式:
- 播放器在播放前,會用該地址
http://domain//hls.key
向業務服務器
發起請求,請求加密m3u8視頻的解密秘鑰
;
解密秘鑰如下圖所示:
加密m3u8視頻的解密秘鑰
並非playKey=i_am_decrypting_key101
,而是一個16字節的文件。
在這里插入圖片描述
m3u8的解密秘鑰實際是一個16字節的文件。
這里肯定會有很多疑問,別着急,我們繼續前邊的話題播放器
與服務端
交互。
播放器 與 服務端 交互
- 當
播放器
向服務端
發起https://domain/course101.m3u8?playKey=i_am_decrypting_key101
請求后; 服務端
處理相對復雜
a、首先校驗playKey=i_am_decrypting_key101
是否有效;
b、若有效則將加密m3u8視頻中的http://domain//hls.key
更換為http://domain//hls.key?playKey=i_am_decrypting_key101
;
c、下發m3u8文件給播放器;播放器
收到服務端
下發的加密m3u8
文件后:
a、播放器
讀取加密m3u8
文件,獲取解密秘鑰
的請求地址http://domain//hls.key?playKey=i_am_decrypting_key101
;
b、播放器
向服務端
發起http://domain//hls.key?playKey=i_am_decrypting_key101
請求,請求解密秘鑰;服務端
收到播放器
的請求后,需對playKey=i_am_decrypting_key101
進行校驗,查看是否過期或者被使用過,若存在異常則不下發秘鑰;若正常,則下發秘鑰播放器
收到秘鑰文件后,正常播放;
OK. 這次是真的 完事大吉
。
4、移動端加固
如果僅僅做到上一步就結束了,移動端被破解的概率還是很高。因為SO任然可以被反編譯破解,而且SO的反編譯技術也已經相當成熟。
所以,無論在SO中采用了怎樣復雜的加密算法,SO被反編譯后,還是有被破解的可能
。
因此,以上步驟都完成后,我們仍然要對我們最終生成的APP進行一次加密。Android端推薦采用APP加固處理
。
5、SO動態下發,定期更新加密算法
以上步驟都做完,如果APP仍然被破解了。我們可以再加一層防破解處理:動態下發SO,定期更新加密算法
。
這樣就算線上APP被破解了,也可以在不發版的情況下從容應對,更新一下加密算法就可以了。
三、總結一下
這里來簡單總結一下,上邊介紹的實現步驟 :
在這里插入圖片描述
- a、移動端拿 courseId 向服務端發起Http請求,
獲取課程的內容列表
; - b、服務端對用戶進行權限驗證,
未購買用戶不返回其對應的播放地址
; - c、移動端
在SO層中對 mediaUrl與 encryptId 進行解密
; - d、移動端 用解密后的encryptId,
向服務端請求對應媒體資源的 playKey
- e、服務端對用戶進行權限驗證,
未購買用戶不返回其對應的 playKey
; - f、移動端
在SO層中對 playKey 進行解密
;
注意,一定要與第三步的解密算法區分開 - g、
拼接mediaUrl 與 playKey
,交給播放器播放;
到此,移動端工作完成。 - h、
播放器
向服務端
發起下發m3u8請求 - i、
服務端
校驗playKey;並動態替換m3u8中的URI字段 - j、
播放器
從m3u8文件中取出URI字段后,向服務端請求解密秘鑰; - k、
服務端
校驗playKey;並返回解密秘鑰文件; - l、
播放器
播放
到此,加密m3u8 終於播放出來了。 - m、移動端App加固處理;
- n、為增強破解難度,這里解密
SO可由動態下發,定期更新加密算法
; - m、如果仍然擔心App被破解,服務端下發的
解密秘鑰文件
可以設置為非明文狀態;客戶端拿到解密秘鑰后,以本地代理的方式設置給播放器進行播放。
注:這里有三個關鍵步驟:1、服務端對用戶進行權限驗證,未購買用戶不返回其對應的播放地址;2、移動端 在SO層中對 playKey 進行解密;3、移動端App加固處理。
這三個關鍵步驟缺一不可,缺少任何一個移動端的破解難度和成本都將大大降低。
- 未購買用戶不返回其對應的播放地址
可以極大的降低付費課程被大面積破解的情況出現。因為購買課程亦需要成本。破解的課程越多,那需要購買的課程就越多,破解成本也就越高。 - 在SO層中對 playKey 進行解密
對於加密m3u8媒體資源,破解的關鍵就在於playKey,因此playKey的加密算法應足夠的復雜 - 移動端App加固處理
移動端App加固,是移動端代碼反編譯非常重要的一環。如果沒有這一環在SO層中對 playKey 進行解密
的作用不是很大,因為無論SO中加密算法如何復雜,只要SO被翻遍后,再復雜的加密算法也沒有意義了。
以上為全部處理流程
如果以上步驟都做了,付費音視頻被盜播的可能性應該不大;但仍然存在Http抓包的可能性:
- http抓包時,會抓到
https://domain/course101.m3u8?playKey=i_am_decrypting_key101
請求
由於playKey只有一次使用有效,或者幾個小時的超時時間。因此該內容無法被大規模傳播; - http抓包時,會抓到播放器的秘鑰下發請求
http://domain//hls.key?playKey=i_am_decrypting_key101
;
由於服務端存在playKey有效性的校驗,下次使用失效,因此也無法大規模傳播;
如果對服務端明文秘鑰文件下發存在疑慮,可以做以上流程圖的“防盜播9”; 防盜播9
加上APP加固
,在實現上應該是比較安全了
如果這樣仍被破解,那我也只能表示佩服 佩服