視訊播放成功率下降很多?可能是你金鑰管理的方式不對!
一、背景
某個客戶原來業務使用了mp3作為播放格式,隨著業務的發展,發現優質的內容經常被成批的下載,這樣對客戶來說是非常嚴重的損失,考慮到使用者的播放需求需要在web瀏覽器也能夠正常播放,以及整體改造成本,最終選擇了HLS標準加密的方案來保護使用者的內容。
接入加密播放以後,發現一個較嚴重的問題,客戶端的播放成功率下降非常多,經過多方排查發現,這是因為特殊字元引發的一個問題。在解密播放的時候我們通過EXT-X-KEY中的URI無法正常獲取到解密的KEY,後者是拿到的KEY不對。所以初步懷疑是業務伺服器對金鑰管理有問題。
本篇文章是由阿里雲視訊雲高階技術專家王海華撰寫,來記錄本次問題的排查、解決方案與後續避免措施。
二、分析排查
通過跟客戶業務伺服器聯調發現:播放器發起如下請求:
QtN2M4ZTAwMjk1ZWUyYVVibnZiTW95QnQzTWJGM1pSK29VVnRlYmNsWEU4aUlBQUFBQUFBQUFBRFhrdE40aUFjUXUxTHptR3V0bTAzVzArVHdGR2pJbU00d0tOSkZmMUtEU080aCtHakhHa1BQ&MtsHlsUriToken=IDXBq4HcS6VGUIh4iyUk3R2QnlGMS2MnWukBTqGhC+oZxdeieVrAHVUU+iwHN1kN" rel="nofollow,noindex" target="_blank">https://api.xxxxx.com/hls/key/dk?MediaId=84a841f61d0d426d8c2058fa72813caa&Ciphertext=YjlhYmQwM2UtMzMxMi00NTgzLWEzMTQtN2M4ZTAwMjk1ZWUyYVVibnZiTW95QnQzTWJGM1pSK29VVnRlYmNsWEU4aUlBQUFBQUFBQUFBRFhrdE40aUFjUXUxTHptR3V0bTAzVzArVHdGR2pJbU00d0tOSkZmMUtEU080aCtHakhHa1BQ&MtsHlsUriToken=IDXBq4HcS6VGUIh4iyUk3R2QnlGMS2MnWukBTqGhC+oZxdeieVrAHVUU+iwHN1kN
返回的是請求非法,根據業務方判斷是MtsHlsUriToken的值不對!
列印了業務服務端獲取到和url請求之前的各自的MtsHlsUriToken引數值發現:
IDXBq4HcS6VGUIh4iyUk3R2QnlGMS2MnWukBTqGhC oZxdeieVrAHVUU iwHN1kN
並非是之前下發的
IDXBq4HcS6VGUIh4iyUk3R2QnlGMS2MnWukBTqGhC+oZxdeieVrAHVUU+iwHN1kN
對比發現非常明顯,url裡面引數的特殊字元被轉義給弄丟失了。跟客戶溝通下來發現使用者的業務服務環境是:spring boot 2.0 ,web容器是Tomcat。通過查閱資料和Tomcat的原始碼發現Tomcat預設會對URL的引數進行URLDecode導致了url裡面的特殊字元被轉義給丟失了,產生了一開始的問題。
整個問題排查過程還是比較簡單的,但是從我們這個場景裡面涉及到的互動非常多,很多環節都需要客戶進行參與,我們如何能保證後續不出現這一類問題呢?我們可以從整個全鏈路的流程上來梳理一下。先看看整個HLS安全播放的整體業務流程。
三、安全播放之HLS標準加密
為了瞭解整件事情過程我們先了解一下整個HLS標準加密業務架構,業務流程和一些技術細節。
1. HLS標準加密之加密(轉碼生產流程)
先看一下我們如何得到一個加密的視訊的。時序圖如下:

名詞解釋
- 業務伺服器:由客戶開發,主要使用者出發轉碼任務和接收回調(播放地址)
- MPS轉碼服務:進行轉碼加密;
- KMS:金鑰管理服務;
- DK:祕鑰明文(使用者加密轉碼和解密播放的真正密文);
- EDK:祕鑰密文(經過加密的DK);
關鍵步驟說明:
- 通過MediaId 從KMS生成祕鑰(DK和EDK),後續可以通過EDK和MediaId重新從KMS中獲取DK;
- 生成m3u8的時候在檔案中加入:#EXT-X-KEY:METHOD=AES-128,並在URI=中新增MediaId + Ciphertext兩個引數,後續業務伺服器可以通過這兩個引數中重新獲得DK;
經過以上的步驟,加密後的視訊(m3u8檔案 和 加密後的ts檔案)已經在OSS中了;
2. HLS標準加密之解密(解密播放流程)
要對視訊進行解密播放有以下幾個關鍵步驟:
- 拿到播放地址;
- 拿到祕鑰;
- 解密播放;
同樣我們也看看整個播放環節的業務流程和一些技術細節。
時序圖如下:

關鍵步驟說明:
- 標準加密視訊播放的時候需要業務伺服器保護和頒發解密祕鑰,獲取播放地址的時候使用者業務伺服器需要判斷請求是否合法,比如是否是登入使用者等;
- 播放地址加上一個引數:MtsHlsUriToken,該引數有業務伺服器生成,該主要用於後續獲取解密key的時候進行合法認證;
- 標準加密場景中,m3u8中會新增擴充套件欄位:#EXT-X-KEY:METHOD=AES-128,URI=,其中的URI一般都是指的是業務伺服器,用來認證請求合法性和返回祕鑰key;
- CDN業務中會自動修改m3u8的檔案,把MtsHlsUriToken引數直接拼接在#EXT-X-KEY:METHOD=AES-128,URI= 之後;
- 播放器拿到 EXT-X-KEY-URI 後請求對應的伺服器並拿到key;
- 播放器用這個key解密後續的ts檔案進行播放;
- 建議:DK可以根據MediaId進行本地快取,沒有必要每次都從KMS中去獲取;
四、後面如何避免?
- 建議MtsHlsUriToken引數值不要帶URL的特殊字元;
- 如果使用者無法避免MtsHlsUriToken重帶有特殊字元則需要對MtsHlsUriToken引數值進行UrlEncode,我們的播放器邏輯和CDN邏輯不對引數做任何的修改;
- 需要讓客戶在對接的時候關注Web容器對URL的Decode處理;
五、附錄衍生知識
1.form的enctype屬性為編碼方式,常用有兩種:application/x-www-form-urlencoded和multipart/form-data,預設為application/x-www-form-urlencoded。
2.如何對Url中的非法字元進行編碼:
Url編碼通常也被稱為百分號編碼(Url Encoding,also known as percent-encoding),是因為它的編碼方式非常簡單,使用%百分號加上兩位的字元——0123456789ABCDEF——代表一個位元組的十六進位制形式。Url編碼預設使用的字符集是US-ASCII。
例如a在US-ASCII碼中對應的位元組是0x61,那麼Url編碼之後得到的就是%61,我們在位址列上輸入 http://g.cn/search?q=%61%62%63 ,實際上就等同於在google上搜索abc了。又如@符號在ASCII字符集中對應的位元組為0x40,經過Url編碼之後得到的是%40。
對於非ASCII字元,需要使用ASCII字符集的超集進行編碼得到相應的位元組,然後對每個位元組執行百分號編碼。對於Unicode字 符,RFC文件建議使用utf-8對其進行編碼得到相應的位元組,然後對每個位元組執行百分號編碼。如"中文"使用UTF-8字符集得到的位元組為0xE4 0xB8 0xAD 0xE6 0x96 0x87,經過Url編碼之後得到"%E4%B8%AD%E6%96%87"。
如果某個位元組對應著ASCII字符集中的某個非保留字元,則此位元組無需使用百分號表示。例如"Url編碼",使用UTF-8編碼得到的位元組是 0x55 0x72 0x6C 0xE7 0xBC 0x96 0xE7 0xA0 0x81,由於前三個位元組對應著ASCII中的非保留字元"Url",因此這三個位元組可以用非保留字元"Url"表示。最終的Url編碼可以簡化 成"Url%E7%BC%96%E7%A0%81" ,當然,如果你用"%55%72%6C%E7%BC%96%E7%A0%81"也是可以的。