编码问题

0x01 编码发展史

  • 0x20以下的编码用作控制码,接下来一直到127号的的字节用来表示大小写字母,标点符号,空格,数字,这就是ascii码,只能满足美国人的需要。
  • 中国人民通过对 ASCII 编码的中文扩充改造,产生了 GB2312 编码,可以表示6000多个常用汉字。
  • 汉字实在是太多了,包括繁体和各种字符,于是产生了 GBK 编码,它包括了 GB2312 中的编码,同时扩充了很多。
  • 中国是个多民族国家,各个民族几乎都有自己独立的语言系统,为了表示那些字符,继续把 GBK 编码扩充为 GB18030 编码。
  • 每个国家都像中国一样,把自己的语言编码,于是出现了各种各样的编码,如果你不安装相应的编码,就无法解释相应编码想表达的内容。
  • 终于,有个叫 ISO 的组织看不下去了。他们一起创造了一种编码 UNICODE ,这种编码非常大,大到可以容纳世界上任何一个文字和标志。所以只要电脑上有 UNICODE 这种编码系统,无论是全球哪种文字,只需要保存文件的时候,保存成 UNICODE 编码就可以被其他电脑正常解释。
  • UNICODE 在网络传输中,出现了两个标准 UTF-8 和 UTF-16,分别每次传输 8个位和 16个位。
  • 于是就会有人产生疑问,UTF-8 既然能保存那么多文字、符号,为什么国内还有这么多使用 GBK 等编码的人?因为 UTF-8 等编码体积比较大,占电脑空间比较多,如果面向的使用人群绝大部分都是中国人,用 GBK 等编码也可以。但是目前的电脑来看,硬盘都是白菜价,电脑性能也已经足够无视这点性能的消耗了。所以推荐所有的网页使用统一编码:UTF-8。

0x02 各种编码

  • URL编码

一个百分号和该字符的ASCII编码所对应的2位十六进制数字,例如“/”的URL编码为%2F(一般大写,但不强求)

具体情况:

网址路径的编码,用的是utf-8编码
查询字符串的编码,用的是操作系统的默认编码
GET和POST方法的编码,用的是网页的编码
在Ajax调用中,IE总是采用GB2312编码(操作系统的默认编码),而Firefox总是采用utf-8编码。

escape()不能直接用于URL编码,它的真正作用是返回一个字符的Unicode编码值。比如"春节"的返回结果是%u6625%u8282,也就是说在Unicode字符集中,"春"是第6625个(十六进制)字符,"节"是第8282个(十六进制)字符。
它的具体规则是,除了ASCII字母、数字、标点符号"@ * _ + - . /"以外,对其他所有字符进行编码。在\u0000到\u00ff之间的符号被转成%xx的形式,其余符号被转成%uxxxx的形式。对应的解码函数是unescape()。
所以,"Hello World"的escape()编码就是"Hello%20World"。因为空格的Unicode值是20(十六进制)。

  • HTML实体编码

命名实体:以&开头,分号结尾的,例如“<”的编码是“<”

字符编码:十进制、十六进制ASCII码或unicode字符编码,样式为“&#数值;”,例如“<”可以编码为“<”和“<”

  • JS编码

js提供了四种字符编码的策略,

三个八进制数字,如果不够个数,前面补0,例如“e”编码为“\145”
两个十六进制数字,如果不够个数,前面补0,例如“e”编码为“\x65”
四个十六进制数字,如果不够个数,前面补0,例如“e”编码为“\u0065”
对于一些控制字符,使用特殊的C类型的转义风格(例如\n和\r)

  • CSS编码

用一个反斜线()后面跟1~6位的十六进制数字,例如e可以编码为“\65”或“65”或“00065”

  • 复合编码

所谓复合编码,也就是说输出的内容输出在多个环境中,例如

’);”>11

value的内容首先出现在一个URL中,这个URL在一段javascript总,而javascript代码又是html的一部分。所以解码的顺序就是HTML解码–>js解码–>url解码,那么正确的编码顺序就应该是url编码–>js编码–>html编码。

0x03 浏览器解析

  • 浏览器解析HTML的步骤

浏览器收到从服务器发送来的HTML内容,会从头解析,当遇到时,会调用javascript脚本解析器解析javascript,并执行脚本,然后继续解析其他的HTML内容,对于一些需要触发才能执行的事件,当触发事件发送时,脚本解析器才会解析其中的脚本,在事件触发之前,它是HTML的一部分。

  • 编码问题实例解析分析

1、URL编码"javascript:alert(1)"

 
Answer: The javascript will NOT execute.

url不能对协议以及协议后的冒号进行编码,否则url解析器会认为其是无类型的。这里JavaScript是个伪协议,而且对javascript进行了url编码,会导致url解析器认为其是无类型,无法进行url解码。最终也就不会弹窗。

2、HTML字符实体编码 "javascript" 和 URL 编码 "alert(2)"


Answer: The javascript will execute.

这里javascript属于html解析中的”属性值中的字符引用“,所以可以先被html解码,然后被url解析器识别出来,后面的alert(2)由于不是协议,url解析器可以正常解析,最后会弹窗。

3、URL 编码 ":"

  
Answer: The javascript will NOT execute.

url不能对协议以及协议后的冒号进行编码,否则url解析器会认为其是无类型的。这里JavaScript是个伪协议,而且对javascript后的冒号进行了url编码,会导致url解析器认为其是无类型,无法进行url解码。最终也就不会弹窗。

4、HTML字符实体编码 < 和 >

  
<img src=x onerror=alert(4)>
Answer: The javascript will NOT execute.

“<”和“>”字符被编码为“<”和“>”。当解析器解析完“

”并处于“数据状态”时,这两个字符将会被解析。当解析器遇到“&”字符,它会知道这是“数据状态的字符引用”,因此会消耗一个字符引用(例如“<”)并释放出对应字符的token。在这个例子中,对应字符指的是“<”和“>”。读者可能会想:这是不是意味着“<”和“>”的token将会被理解为标签的开始和结束,然后其中的脚本会被执行?答案是脚本并不会被执行。原因是解析器在解析这个字符引用后不会转换到“标签开始状态”。正因为如此,就不会建立新标签。因此,我们能够利用字符实体编码这个行为来转义用户输入的数据从而确保用户输入的数据只能被解析成“数据”。

5、HTML字符实体编码 < 和 >

  
Answer: The javascript will NOT execute AND the character entities will NOT be decoded either

Answer: The javascript will NOT execute.

在浏览器解析RCDATA元素的过程中,解析器会进入“RCDATA状态”。在这个状态中,如果遇到“<”字符,它会转换到“RCDATA小于号状态”。如果“<”字符后没有紧跟着“/”和对应的标签名,解析器会转换回“RCDATA状态”。这意味着在RCDATA元素标签的内容中(例如”或者“”。当然,这要看开始标签是哪一个。因此,在“