HTTP Content-MD5 首部欄位:編碼的坑
在《圖解 HTTP》(上野 宣 著,於均良 譯,人民郵電出版社,2014年4月,ISBN 9787115351531)第129頁(6.6.6節),作者給出了一則首部示例:
Content-MD5: OGFkZDUwNGVhNGY3N2MxMDIwZmQ4NTBmY2IyTY==
和廣為流傳的例子 Content-MD5: Q2hlY2sgSW50ZWdyaXR5IQ==
比較一下,
大~家~有~沒~有~覺~得~有~點~長?
MD5 作為校驗碼,是一個 128 位長的二進位制數。 在記憶體中,128 bits = 16 octets。 經過 Base64 編碼,長度增加約 33%,編碼後的長度應為 4*⌈16/3⌉ = 24 位元組。 而書中栗子的長度為 40 位元組。
原來這裡也有個坑!
演算法細節
HTTP/1.1(RFC2616#14.15)給出了實體首部欄位 Content-MD5
的語法規則:
Content-MD5 = "Content-MD5" ":" md5-digest md5-digest = <base64 of 128 bit MD5 digest as per RFC 1864>
即校驗值的編碼依據為 RFC1864:
MD5 演算法輸出的結果為 128 位長的摘要。當以網路位元組序(大端序) 解析時,可得到16位元組的二進位制資料序列。隨後,將這 16 個位元組按 base64 演算法編碼,最終得到可作為 `Content-MD5` 欄位取值的結果。因此,針對 MIME 實體的原始資料應用 MD5 演算法,若得到的摘要值為(幾乎不可能的)"Check Integrity!"注 1,則該 MIME 實體的首部可包含此欄位:Content-MD5: Q2hlY2sgSW50ZWdyaXR5IQ==注 2
這是一個16位元組長的字串,對應的 32 字元表示為 0x
436865636b20496e7465677269747921
。(筆者查詢了多個逆向庫,均尚未收錄 md5 為該值的資料。)“Check Integrity!”作為 ASCII 字串的 base64 編碼,長度為 24 位元組。
看吧,RFC1864 給出的例子也是 24 位元組長。 那麼問題出在哪裡?
剛才點選了 Base64 連結的讀者可能已經通過實驗發現答案了:
書中給出的欄位值 OGFkZDUwNGVhNGY3N2MxMDIwZmQ4NTBmY2IyTY==
是對字串 "8add504ea4f77c1020fd850fcb2M
"注的 base64 編碼,而不是對二進位制數 0x8add504ea4f77c1020fd850fcb2?
注 的 base64 編碼。
注:實際解碼過程中出現了錯誤(所以出現了比較顯眼的“M”字母)。該 28 位結果是報錯前的部分輸出,但不影響分析問題。
在實踐中,應注意先取得 md5 校驗值的二進位制表示,然後進行 base64 編碼。例如:
PHP 中使用
md5($資料, true)
而非md5($資料)
(via StackOverflow)ColdFusion 中使用
binaryDecode(校驗碼字串, "hex")
再用binaryEncode(二進位制資料, "base64")
(via Ben Nadel)Java 中使用
DigestInputStream.getMessageDigest().digest()
而不是隱式呼叫DigestInputStream.getMessageDigest().toString()
(via Amazon MWS)
補充知識
欄位名大小寫
HTTP/1.1(RFC2616#4.2)說了,欄位名不區分大小寫。所以即使寫成
cOnTeNt-mD5
也不會有什麼問題。作為 HTTP/2 實驗場的 SPDY 協議(最新草案為 3.2)規定,所有欄位名均必須小寫。
一定要用 base64 編碼嗎?
目前使用 Content-MD5
首部的大流量服務商只有兩家:亞馬遜和百度。(沒錯!)
雖然我把百度列在後面,並不意味著百度雲的流量比亞馬遜雲小。
在網際網路這個世界,的確是誰的嗓門大誰決定行業準則。
亞馬遜嚴格遵守 RFC1864,不僅在下載時提供校驗值,還對上傳檔案的校驗值和請求中的值進行二次核對。
百度雲網盤更貼近民生,直接在響應報文中返回 md5 校驗碼的十六進位制表示(32 位元組),看起來多方便呀!
ETag: b0d95dbfeeb97fa7e411aba81729229f Content-MD5: b0d95dbfeeb97fa7e411aba81729229f
32 只比 24 多 8 個位元組,意義大不一樣。何必死死遵守那個並不是為 HTTP 設計的方案呢?
(如果 Firefox 等保皇派拒絕相容,給欄位名加一個 X-
字首得了。)
相關文章
- HTTP首部欄位詳解HTTP
- 常見Http首部欄位HTTP
- 通用首部欄位詳解-四大首部欄位之一
- 有關 HTTP 快取的首部欄位說一下HTTP快取
- 請求首部欄位詳解-四大首部欄位之一
- HTTP首部HTTP
- HTTP首部解析HTTP
- HTTP首部(二)HTTP
- HTTP header 欄位解釋HTTPHeader
- HTTP請求頭中的refer欄位HTTP
- 05 前端HTTP協議(圖解HTTP) 之 HTTP首部前端HTTP協議圖解
- mysql 資料庫 表 欄位 編碼修改 方法MySql資料庫
- 《圖解HTTP》讀書筆記四:HTTP 首部圖解HTTP筆記
- HTTP請求頭的Content-Type欄位HTTP
- HTTP協議---HTTP請求中的常用請求欄位和HTTP的響應狀態碼及響應頭HTTP協議
- http請求頭個欄位解釋HTTP
- node編碼中的坑
- Mysql 的編碼方式使得欄位內容不區分大小寫MySql
- SAP Spartacus UI 透過 HTTP Interceptor 給請求新增 Authorization 欄位的原始碼分析UIHTTP原始碼
- 簡述 HTTP 快取首部及其行為HTTP快取
- Dynamics 365 可編輯子網格的欄位禁用不可編輯
- 構建高效能WEB之HTTP首部優化WebHTTP優化
- HTTP 請求響應頭部欄位裡 ETAG 的用法舉例HTTP
- 增加欄位的通用程式碼驗證功能
- 【Mongo】mongo更新欄位為另一欄位的值Go
- MSWord-用欄位程式碼混淆
- 欄位型別檢測指令碼型別指令碼
- 補充行業程式碼欄位行業
- 多執行緒迴圈控制欄位失效造成死迴圈的坑執行緒
- 鮮為人知的HTTP協議頭欄位詳解大全「原創」HTTP協議
- .net 6 使用 NEST 查詢,時間欄位傳值踩坑
- 巧用欄位對映實現指定欄位的搜尋
- SAP定價的合計欄位的程式碼照抄
- MySQL5.7密碼欄位變更MySql密碼
- 密碼欄位,使用md5加密,欄位儲存用raw,避免字符集的問題密碼加密
- HTTP 請求頭部欄位中 connection - keep-alive 的含義HTTPKeep-Alive
- MySQL欄位新增註釋,但不改變欄位的型別MySql型別
- 欄位排序排序