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 header 欄位解釋HTTPHeader
- 05 前端HTTP協議(圖解HTTP) 之 HTTP首部前端HTTP協議圖解
- HTTP請求頭的Content-Type欄位HTTP
- proto裡的message欄位的編號
- http請求頭個欄位解釋HTTP
- node編碼中的坑
- SAP Spartacus UI 透過 HTTP Interceptor 給請求新增 Authorization 欄位的原始碼分析UIHTTP原始碼
- 簡述 HTTP 快取首部及其行為HTTP快取
- pydantic 欄位欄位校驗
- Dynamics 365 可編輯子網格的欄位禁用不可編輯
- HTTP 請求響應頭部欄位裡 ETAG 的用法舉例HTTP
- HTTP 請求頭部欄位中 connection - keep-alive 的含義HTTPKeep-Alive
- 【Mongo】mongo更新欄位為另一欄位的值Go
- 補充行業程式碼欄位行業
- 鮮為人知的HTTP協議頭欄位詳解大全「原創」HTTP協議
- 多執行緒迴圈控制欄位失效造成死迴圈的坑執行緒
- SAP定價的合計欄位的程式碼照抄
- fastadmin 新增欄位記圖片欄位AST
- .net 6 使用 NEST 查詢,時間欄位傳值踩坑
- HTTP 頭部欄位 Cache Control max-age = 0 和 no-cache 的區別HTTP
- MySQL5.7密碼欄位變更MySql密碼
- 編輯功能-載荷裡空欄位沒有傳
- MySQL欄位新增註釋,但不改變欄位的型別MySql型別
- 使用apache的HttpClient進行http通訊,隱藏的HTTP請求頭部欄位是如何自動被新增的ApacheHTTPclient
- SAP ABAP 中,if_http_extension 介面的flow_rc 欄位含義HTTP
- logstash 收集 http POST請求中的json日誌時,欄位衝突問題HTTPJSON
- Oracle-欄位的新增Oracle
- abc欄位數的使用
- 避坑手冊 | JAVA編碼中容易踩坑的十大陷阱Java
- Spring Cloud Gateway過濾器精確控制異常返回(實戰,控制http返回碼和message欄位)SpringCloudGateway過濾器HTTP
- [BUG反饋]模型管理 > 欄位管理看不見任何欄位。這表明顯有欄位、!模型
- 電位器和編碼器的區別
- [譯][草案] HTTP “帶外”內容編碼HTTP