深入理解浏览器解析机制和XSS向量编码

基础部分

1.<a href="%6a%61%76%61%73%63%72%69%70%74:%61%6c%65%72%74%28%31%29">aaa</a>

<a>标签可以识别,但是解析不了, 是在协议的编码顺序上,先认协议

URL 编码 "javascript:alert(1)"

2.<a href="&#x6a;&#x61;&#x76;&#x61;&#x73;&#x63;&#x72;&#x69;&#x70;&#x74;:%61%6c%65%72%74%28%32%29">

HTML字符实体编码 "javascript" 和 URL 编码 "alert(2)" (实体协议编码优先级更高)

3.<a href="javascript%3aalert(3)"></a>

URL 编码 ":" 协议是不能被编码的,因为无法解码,所以无法实现

4.<div>&#60;img src=x οnerrοr=alert(4)&#62;</div>

解码:<img src ="1" onerror ="alert(4)">

HTML字符实体编码 < 和 > 当我们进入数据状态中的字符引用时,确实可以将编码字符进行编码,但是不会进入数据状态中,可以显示,但无法执行

5.<textarea>&#60;script&#62;alert(5)&#60;/script&#62;</textarea>

HTML字符实体编码 < 和 >

6.<textarea><script>alert(6)</script></textarea>

</script>只是一个字符串,不认为他是个标签

高级部分

7.<button οnclick="confirm('7&#39;);">Button</button>

HTML字符实体编码 " ' " (单引号)

8.<button οnclick="confirm('8\u0027);">Button</button>

Unicode编码 " ' " (单引号) 无法执行

严格区分大小写,不能编码符号

9.<script>&#97;&#108;&#101;&#114;&#116;&#40;&#57;&#41;&#59;</script>

HTML字符实体编码 alert(9); 无法执行

10.<script>\u0061\u006c\u0065\u0072\u0074(10);</script>

Unicode 编码 alert 直接执行

11.<script>\u0061\u006c\u0065\u0072\u0074\u0028\u0031\u0031\u0029</script>

Unicode 编码 alert(11) 无法执行 (语法错误)

12.<script>\u0061\u006c\u0065\u0072\u0074(\u0031\u0032)</script>

Unicode 编码 alert 和 12 无法执行(语法错误)

13.<script>alert('13\u0027)</script>

Unicode 编码 " ' " (单引号) 无法执行 (语法错误)

14.<script>alert('14')</script>

Unicode 编码换行符(0x0A)

15.<a href="&#x6a;&#x61;&#x76;&#x61;&#x73;&#x63;&#x72;&#x69;&#x70;&#x74;&#x3a;&#x25;&#x35;&#x63;&#x25;&#x37;&#x35;&#x25;&#x33;&#x30;&#x25;&#x33;&#x30;&#x25;&#x33;&#x36;&#x25;&#x33;&#x31;&#x25;&#x35;&#x63;&#x25;&#x37;&#x35;&#x25;&#x33;&#x30;&#x25;&#x33;&#x30;&#x25;&#x33;&#x36;&#x25;&#x36;&#x33;&#x25;&#x35;&#x63;&#x25;&#x37;&#x35;&#x25;&#x33;&#x30;&#x25;&#x33;&#x30;&#x25;&#x33;&#x36;&#x25;&#x33;&#x35;&#x25;&#x35;&#x63;&#x25;&#x37;&#x35;&#x25;&#x33;&#x30;&#x25;&#x33;&#x30;&#x25;&#x33;&#x37;&#x25;&#x33;&#x32;&#x25;&#x35;&#x63;&#x25;&#x37;&#x35;&#x25;&#x33;&#x30;&#x25;&#x33;&#x30;&#x25;&#x33;&#x37;&#x25;&#x33;&#x34;&#x28;&#x31;&#x35;&#x29;"></a>

三层解码:<img src ="1" onerror ="alert(15)">

在解析一篇HTML文档时主要有三个处理过程:HTML解析,URL解析和JavaScript解析。每个解析器负责解码和解析HTML文档中它所对应的部分,其工作原理已经在相应的解析器规范中明确写明。

HTML解析

从XSS的角度来说,我们感兴趣的是HTML文档是如何被词法解析的,因为我们并不想让用户提供的数据最终被解析为一段可执行脚本的script标签。HTML词法解析细则在

(http://www.whatwg.org/specs/web-apps/current-work/multipage/tokenization.html)。

HTML词法解析细则是一篇冗长的文档,这篇博文并不会覆盖它的所有内容。这篇博文只会覆盖有关文档解码如何结束,以及新token何时被创建这两个有趣的部分。

<input value="dasdsad"> 2021 cure53 Dompurity

一个HTML解析器作为一个状态机,它从输入流中获取字符并按照转换规则转换到另一种状态。在解析过程中,任何时候它只要遇到一个'<'符号(后面没有跟'/'符号)就会进入"标签开始状态(Tag open state)"。然后转变到"标签名状态(Tag name state)","前属性名状态(before attribute name state)"......最后进入"数据状态(Data state)"并释放当前标签的token。当解析器处于"数据状态(Data state)"时,它会继续解析,每当发现一个完整的标签,就会释放出一个token。

字符实体(character entities)**

字符实体是一个转义序列,它定义了一般无法在文本内容中输入的单个字符或符号。一个字符实体以一个&符号开始,后面跟着一个预定义的实体的名称,或是一个#符号以及字符的十进制数字。 **HTML字符实体(HTML character entities)**

在HTML中,某些字符是预留的。例如在HTML中不能使用"<"或">",这是因为浏览器可能误认为它们是标签的开始或结束。需要注意的是,某些字符没有实体名称,但可以有实体编号。

字符引用(character references)**

字符引用包括"字符值引用"和"字符实体引用"。在上述HTML例子中,'<'对应的字符值引用为'&#60',对应的字符实体引用为'&lt'。字符实体引用也被叫做"实体引用"或"实体"。)

现在你大概会明白为什么我们要转义"<"、">"、"'" (单引号)和""" (双引号)字符了。

在HTML中有五类元素:

  1. 空元素(Void elements),如<area>,<br>,<base>等等

  2. 原始文本元素(Raw text elements),有<script>和<style>

  3. RCDATA元素(RCDATA elements),有<textarea>和<title>

4.外部元素(Foreign elements),例如MathML命名空间或者SVG命名空间的元素

5.基本元素(Normal elements),即除了以上4种元素以外的元素

五类元素的区别如下:

  1. 空元素,不能容纳任何内容(因为它们没有闭合标签,没有内容能够放在开始标签和闭合标签中间)。

  2. 原始文本元素,可以容纳文本。

  3. RCDATA元素,可以容纳文本和字符引用。

  4. 外部元素,可以容纳文本、字符引用、CDATA段、其他元素和注释

  5. 基本元素,可以容纳文本、字符引用、其他元素和注释

URL解析

URL解析器也是一个状态机模型,从输入流中进来的字符可以引导URL解析器转换到不同的状态。解析器的解析细则在(http://url.spec.whatwg.org/)。其中有很多有关安全或XSS转义的内容。

首先,URL资源类型必须是ASCII字母(U+0041-U+005A || U+0061-U+007A),不然就会进入"无类型"状态。

如果你记得我们在HTML解析部分讨论的内容的话,是否还记得有一个情况叫做"属性值中的字符引用",在这个情况中字符引用会被解码。我们将稍后讨论解析顺序,但在这里,HTML解析器解析了文档,创建了标签token,并且对href属性里的字符实体进行了解码。然后,当HTML解析器工作完成后,URL解析器开始解析href属性值里的链接。在这时,"javascript"协议已经被解码,它能够被URL解析器正确识别。然后URL解析器继续解析链接剩下的部分。由于是"javascript"协议,JavaScript解析器开始工作并执行这段代码。

其次,URL编码过程使用UTF-8编码类型来编码每一个字符。如果你尝试着将URL链接做了其他编码类型的编码,URL解析器就可能不会正确识别。

JavaScript 解析

JavaScript解析过程与HTML解析过程有点不一样。JavaScript语言是一门内容无关语言。对应着有一份内容无关的语法来描述它。我们可以利用内容无关语法来解释JavaScript是如何解析的。ECMAScript-262细则在(http://www.ecma-international.org/publications/standards/Ecma-262.htm),语法文件在(http://www.antlr3.org/grammar/1206736738015/JavaScript.g)。

**所有的"script"块都属于"原始文本"元素。"script"块有个有趣的属性:在块中的字符引用并不会被解析和解码。**如果你去看"脚本数据状态"的状态转换规则,就会发现没有任何规则能转移到字符引用状态。

**字符串中:**当Unicode转义序列存在于字符串中时,它只会被解释为正规字符,而不是单引号,双引号或者换行符这些能够打破字符串上下文的字符。这项内容清楚地写在ECMAScript中。因此,Unicode转义序列将永远不会破环字符串上下文,因为它们只能被解释成字符串常量。

**"ECMAScript 与 JAVA 编程语言在对待Unicode转义序列时的行为不同。**在Java程序中,如果Unicode转义序列\u000A出现在单行字符串注释中,它会被解释为行结束符(换行符),因此会导致接下来的Unicode字符不是注释的一部分。同样的,如果Unicode转义序列\u000A出现在Java程序的字符串常量中,它同样会被解释为行结束符(换行符),这在字符串常量中是不被允许的------如果需要在字符串常量中表示换行,需要用\n来代替\u000A。在ECMAScript程序中,出现在注释中的Unicode转义序列永远不会被解释,因此不会导致注释换行问题。同样地,ECMAScript程序中,在字符串常量中出现的Unicode转义序列会被当作字符串常量中的一个Unicode字符,并且不会被解释成有可能结束字符串常量的换行符或者引号。"

标识符名称中:当Unicode转义序列出现在标识符名称中时,它会被解码并解释为标识符名称的一部分,例如函数名,属性名等等。

控制字符:当用Unicode转义序列来表示一个控制字符时,例如单引号、双引号、圆括号等等,它们将不会被解释成控制字符,而仅仅被解码并解析为标识符名称或者字符串常量。

解析流

在讨论过HTML,URL和JavaScript解析之后,读者应该能够对"什么会被解码"、"在什么地方被解码"和"如何被解码"这几件事有了清楚的认识。现在,另一个重要的概念是所有这些是如何协同工作的?在网页中有很多地方需要多个解析器来协同工作。因此,对于解码和转义问题,我们将简要的讨论浏览器如何解析一篇文档。

当浏览器从网络堆栈中获得一段内容后,触发HTML解析器来对这篇文档进行词法解析。在这一步中字符引用被解码。在词法解析完成后,DOM树就被创建好了,JavaScript解析器会介入来对内联脚本进行解析。在这一步中Unicode转义序列和Hex转义序列被解码。同时,如果浏览器遇到需要URL的上下文,URL解析器也会介入来解码URL内容。在这一步中URL解码操作被完成。由于URL位置不同,URL解析器可能会在JavaScript解析器之前或之后进行解析。考虑如下两种情况

Example A: <a href="UserInput"></a>

Example B: <a href=# οnclick="window.open('UserInput')"></a>

在例A中,HTML解析器将首先开始工作,并对UserInput中的字符引用进行解码。然后URL解析器开始对href值进行URL解码。最后,如果URL资源类型是JavaScript,那么JavaScript解析器会进行Unicode转义序列和Hex转义序列的解码。再之后,解码的脚本会被执行。因此,**这里涉及三轮解码,顺序是HTML,URL和JavaScript**。

在例B中,HTML解析器首先工作。然而接下来,JavaScript解析器开始解析在onclick事件处理器中的值。这是因为在onclick事件处理器中是script的上下文。当这段JavaScript被解析并被执行的时候,它执行的是"window.open()"操作,其中的参数是URL的上下文。在此时,URL解析器开始对UserInput进行URL解码并把结果回传给JavaScript引擎。因此这里一共涉及三轮解码,顺序是HTML,JavaScript和URL。

有没有可能解码次数超过3轮呢?考虑一下这个例子

Example C: <a href="javascript:window.open('UserInput')">

例C与例A很像,但不同的是在UserInput前多了window.open()操作。因此,对UserInput多了一次额外的URL解码操作。总的来说,四轮解码操作被完成,顺序是HTML,URL,JavaScript和URL。

相关推荐
前端Hardy21 分钟前
纯HTML&CSS实现3D旋转地球
前端·javascript·css·3d·html
susu108301891124 分钟前
vue3中父div设置display flex,2个子div重叠
前端·javascript·vue.js
弗拉唐1 小时前
springBoot,mp,ssm整合案例
java·spring boot·mybatis
oi771 小时前
使用itextpdf进行pdf模版填充中文文本时部分字不显示问题
java·服务器
少说多做3432 小时前
Android 不同情况下使用 runOnUiThread
android·java
知兀2 小时前
Java的方法、基本和引用数据类型
java·笔记·黑马程序员
蓝黑20202 小时前
IntelliJ IDEA常用快捷键
java·ide·intellij-idea
Ysjt | 深2 小时前
C++多线程编程入门教程(优质版)
java·开发语言·jvm·c++
shuangrenlong2 小时前
slice介绍slice查看器
java·ubuntu