转载于:http://www.ituring.com.cn/article/74167
在《图解 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-
前缀得了。