深入理解浏览器解析机制和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。

相关推荐
激流丶6 分钟前
【Kafka 实战】如何解决Kafka Topic数量过多带来的性能问题?
java·大数据·kafka·topic
Themberfue9 分钟前
Java多线程详解⑤(全程干货!!!)线程安全问题 || 锁 || synchronized
java·开发语言·线程·多线程·synchronized·
让学习成为一种生活方式26 分钟前
R包下载太慢安装中止的解决策略-R语言003
java·数据库·r语言
web行路人31 分钟前
React中类组件和函数组件的理解和区别
前端·javascript·react.js·前端框架
番茄小酱00132 分钟前
Expo|ReactNative 中实现扫描二维码功能
javascript·react native·react.js
晨曦_子画32 分钟前
编程语言之战:AI 之后的 Kotlin 与 Java
android·java·开发语言·人工智能·kotlin
子非鱼9211 小时前
【Ajax】跨域
javascript·ajax·cors·jsonp
超雄代码狂1 小时前
ajax关于axios库的运用小案例
前端·javascript·ajax
南宫生1 小时前
贪心算法习题其三【力扣】【算法学习day.20】
java·数据结构·学习·算法·leetcode·贪心算法
Heavydrink1 小时前
HTTP动词与状态码
java