我最近经历了utf8解码的go源代码。 显然,当解码utf8字节时,第一个字节的值为224 (0xE0)它映射到[0xA0;为0xBF。 https://github.com/golang/go/blob/master/src/unicode/utf8/utf8.go#L81 https://github.com/golang/go/blob/master/src/unicode/utf8/utf8.go#L94
如果我正确理解utf8规范(https://tools.ietf.org/html/rfc3629),则每个连续字节的最小值为0x80或1000 0000.为什么打开0xE0的字节的最小值更高,即0xA0而不是0x80?
答案 0 :(得分:4)
原因是为了防止所谓的过长序列。引用RFC:
上面的解码算法的实现必须防止 解码无效序列。例如,一个天真的实现可能 将超长UTF-8序列C0 80解码为字符U + 0000 , 或代理对ED A1 8C ED BE B4进入U + 233B4。解码 无效序列可能会产生安全后果或导致其他后果 问题。
[...]
可以针对这种攻击进行特别微妙的攻击 一个解析器,它执行安全关键的有效性检查 UTF-8编码形式的输入,但解释某些非法八位字节 序列作为字符。例如,解析器可能会禁止 编码为单八位字节序列00时的NUL字符,但是 错误地允许非法的两个八位字节序列C0 80并解释 它是一个NUL角色。另一个例子可能是解析器 禁止八位字节序列2F 2E 2E 2F(“/../”),但允许 非法八位字节序列2F C0 AE 2E 2F。最后一次攻击有 实际上已被广泛用于攻击Web服务器的病毒 2001;因此,安全威胁是非常真实的。
另请注意第4节中的语法规则,它明确地仅允许在E0之后的字符A0-BF:
UTF8-2 = %xC2-DF UTF8-tail UTF8-3 = %xE0 %xA0-BF UTF8-tail / %xE1-EC 2( UTF8-tail ) / %xED %x80-9F UTF8-tail / %xEE-EF 2( UTF8-tail ) UTF8-4 = %xF0 %x90-BF 2( UTF8-tail ) / %xF1-F3 3( UTF8-tail ) / %xF4 %x80-8F 2( UTF8-tail )
答案 1 :(得分:1)
如果UTF-8序列的第一个字节是0xe0
,这意味着它是一个3字节序列,表示/编码Unicode代码点(因为0xe0 = 1110 0000b
)。
Number Bits for First Last Byte 1 Byte 2 Byte 3
of bytes code point code point code point
---------------------------------------------------------------------------
3 16 U+0800 U+FFFF 1110xxxx 10xxxxxx 10xxxxxx
使用3字节UTF-8序列编码的第一个Unicode代码点是U + 0800,因此代码点为0x0800
,二进制:0000 1000 0000 0000
如果您尝试将这些位插入标有x
的那些位:
1110xxxx 10xxxxxx 10xxxxxx
11110000 10100000 10000000
如您所见,第二个字节为1010 0000
,即0xa0
。所以一个以0xe0
开头的有效UTF-8序列:它的第二个字节不能低于0xa0
(因为即使是UTF-8编码序列以0xe0
开头的最低Unicode码点也有一个0xa0
)的第二个字节。