目录
浏览器解析机制遵循规则:
- HTML解析
- URL解析
- JavaScript解析
解析顺序是HTML——>URL——>JavaScript。
HTML解析
HTML词法解析细则:
HTML Standardhttps://html.spec.whatwg.org/multipage/parsing.html一个HTML解析器作为一个状态机,它从输入流中获取字符并按照转换规则转换到另一种状态。在解析过程中,任何时候它只要遇到一个'<'符号(后面没有跟'/'符号)就会进入“标签开始状态(Tag open state)”。然后转变到“标签名状态(Tag name state)”,“前属性名状态(before attribute name state)”......最后进入“数据状态(Data state)”并释放当前标签的token。当解析器处于“数据状态(Data state)”时,它会继续解析,每当发现一个完整的标签,就会释放出一个token。
这里有三种情况可以容纳字符实体,“数据状态中的字符引用”,“RCDATA状态中的字符引用”和“属性值状态中的字符引用”。在这些状态中HTML字符实体将会从“&#...”形式解码,对应的解码字符会被放入数据缓冲区中。(这些后面很重要)“<”和“>”字符被编码为“<”和“>”。当解析器解析完“<div>”并处于“数据状态”时,这两个字符将会被解析。当解析器遇到“&”字符,它会知道这是“数据状态的字符引用”,因此会消耗一个字符引用(例如“<”)并释放出对应字符的token。 解析器在解析这个字符引用后不会转换到“标签开始状态”。因此,我们能够利用字符实体编码这个行为来转义用户输入的数据从而确保用户输入的数据只能被解析成“数据”。
解释几个概念
字符实体(character entities)
字符实体是一个转义序列,它定义了一般无法在文本内容中输入的单个字符或符号。一个字符实体以一个&符号开始,后面跟着一个预定义的实体的名称,或是一个#符号以及字符的十进制数字。
HTML字符实体(HTML character entities)
在HTML中,某些字符是预留的。例如在HTML中不能使用“<”或“>”,这是因为浏览器可能误认为它们是标签的开始或结束。如果希望正确地显示预留字符,就需要在HTML中使用对应的字符实体。一个HTML字符实体描述如下:
字符引用(character references)
字符引用包括“字符值引用”和“字符实体引用”。在上述HTML例子中,'<'对应的字符值引用为'<',对应的字符实体引用为‘<’。字符实体引用也被叫做“实体引用”或“实体”。)
现在你大概会明白为什么我们要转义“<”、“>”、“'” (单引号)和“"” (双引号)字符了。
这里要提一下RCDATA的概念。要了解什么是RCDATA,我们先要了解另一个概念。在HTML中有五类元素:
空元素(Void elements),如<area>, ,<base>等等
原始文本元素(Raw text elements),有<script>和<style>
RCDATA元素(RCDATA elements),有<textarea>和<title>
外部元素(Foreign elements),例如MathML命名空间或者SVG命名空间的元素
基本元素(Normal elements),即除了以上4种元素以外的元素
五类元素的区别如下:
空元素,不能容纳任何内容(因为它们没有闭合标签,没有内容能够放在开始标签和闭合标签中间)。
原始文本元素,可以容纳文本。
RCDATA元素,可以容纳文本和字符引用。
外部元素,可以容纳文本、字符引用、CDATA段、其他元素和注释
基本元素,可以容纳文本、字符引用、其他元素和注释
命名空间:
1:svg——画布
SVG namespacehttps://www.w3.org/2000/svg
2:html5
3:math——接收数学公式
使用<svg>会切换到<svg>的命名空间内,遵循<svg>的语法格式(xml格式)。这三个命名空间可以相互切换。
URL解析
URL解析细则:
URL Standardhttps://url.spec.whatwg.org/URL资源类型必须是ASCII字母(U+0041-U+005A || U+0061-U+007A),不然就会进入“无类型”状态。例如,你不能对协议类型进行任何的编码操作,不然URL解析器会认为它无类型。
其次,URL编码过程使用UTF-8编码类型来编码每一个字符。如果你尝试着将URL链接做了其他编码类型的编码,URL解析器可能不会正确识别
编码形式如:\u0061\u006c\u0065\u0072\u0074 编码成:%5c%75%30%30%36%31%5c%75%30%30%36%63%5c%75%30%30%36%35%5c%75%30%30%37%32%5c%75%30%30%37%34
JavaScript解析
JavaScript语法文件 JavaScript.ghttp://www.antlr3.org/grammar/1206736738015/JavaScript.g
编码形式如:alert 编码成:\u0061\u006c\u0065\u0072
XSS Payload举例:
案例原文:
一:
<a href="%6a%61%76%61%73%63%72%69%70%74:%61%6c%65%72%74%28%31%29"></a>
<a href="%6a%61%76%61%73%63%72%69%70%74:%61%6c%65%72%74%28%31%29"></a>
URL encoded "javascript:alert(1)"
Answer: The javascript will NOT execute.
里面没有HTML编码内容,不考虑,其中href内部是URL,于是直接丢给URL模块处理,但是协议无法识别(即被编码的javascript:
),解码失败,不会被执行。URL资源类型必须是ASCII字母(U+0041-U+005A || U+0061-U+007A),URL中被编码的“javascript”没有被解码,因此不会被URL解析器识别。
URL规定协议:URL Standardhttps://url.spec.whatwg.org/#concept-url
二:
<a href="javascript:%61%6c%65%72%74%28%32%29">
<a href="javascript:%61%6c%65%72%74%28%32%29">
先HTML解码,得到
<a href="javascript:%61%6c%65%72%74%28%32%29">
href中为URL,URL模块可识别为javascript
协议,进行URL解码,得到
<a href="javascript:alert(2)">
由于是javascript协议,解码完给JS模块处理,于是被执行
三:
<a href="javascript%3aalert(3)"></a>
<a href="javascript%3aalert(3)"></a>
URL encoded ":"
Answer: The javascript will NOT execute.
与一类似:因为URL中被编码的** : **没有被解码,协议无法识别(即被编码的javascript:
),因此不会被URL解析器识别。
URL资源类型必须是ASCII字母(U+0041-U+005A || U+0061-U+007A),不然就会进入“无类型”状态。例如,你不能对协议类型进行任何的编码操作,不然URL解析器会认为它无类型。这就是为什么问题1中的代码不能被执行。因为URL中被编码的“javascript”没有被解码,因此不会被URL解析器识别。该原则对协议后面的“:”(冒号)同样适用,即问题3也得到解答。
四:
<div><img src=x οnerrοr=alert(4)></div>
<div><img src=x onerror=alert(4)></div>
<img src=x οnerrοr=alert(4)>
字符实体编码 <和 >
回答: javascript 将不会执行。
这里包含了HTML编码内容,反过来以开发者的角度思考,HTML编码就是为了显示这些特殊字符,而不干扰正常的DOM解析,所以这里面的内容不会变成一个img元素,也不会被执行
从HTML解析机制看,在读取<div>
之后进入数据状态,<
会被HTML解码,但不会进入标签开始状态,当然也就不会创建img
元素,也就不会执行。
“<”和“>”字符被编码为“<”和“>”。当解析器解析完“<div>”并处于“数据状态”时,这两个字符将会被解析。当解析器遇到“&”字符,它会知道这是“数据状态的字符引用”,因此会消耗一个字符引用(例如“<”)并释放出对应字符的token。在这个例子中,对应字符指的是“<”和“>”。读者可能会想:这是不是意味着“<”和“>”的token将会被理解为标签的开始和结束,然后其中的脚本会被执行?答案是脚本并不会被执行。原因是解析器在解析这个字符引用后不会转换到“标签开始状态”。正因为如此,就不会建立新标签。因此,我们能够利用字符实体编码这个行为来转义用户输入的数据从而确保用户输入的数据只能被解析成“数据”。
五:
<textarea><script>alert(5)</script></textarea>
<textarea><script>alert(5)</script></textarea>
Character entity encoded < and >
Answer: The javascript will NOT execute AND the character entities will NOT be decoded either(javascript 不会执行,字符实体也不会被解码)
<textarea>
是RCDATA
元素(RCDATA elements),可以容纳文本和字符引用,注意不能容纳其他元素,HTML解码得到
<textarea><script>alert(5)</script></textarea>
于是直接显示
RCDATA`元素(RCDATA elements)包括`textarea`和`title
回头看HTML解析器的规则,其中有一种可以容纳字符引用的情况是“RCDATA状态中的字符引用”。这意味着在<textarea>和<title>标签中的字符引用会被HTML解析器解码。这里要再提醒一次,在解析这些字符引用的过程中不会进入“标签开始状态”。
另外,对RCDATA有个特殊的情况。在浏览器解析RCDATA元素的过程中,解析器会进入“RCDATA状态”。在这个状态中,如果遇到“<”字符,它会转换到“RCDATA小于号状态”。如果“<”字符后没有紧跟着“/”和对应的标签名,解析器会转换回“RCDATA状态”。这意味着在RCDATA元素标签的内容中(例如<textarea>或<title>的内容中),唯一能够被解析器认做是标签的就是“</textarea>”或者“</title>”。因此,在“<textarea>”和“<title>”的内容中不会创建标签,就不会有脚本能够执行
六:
<textarea><script>alert(6)</script></textarea>
<textarea><script>alert(6)</script></textarea>
Answer: The javascript will NOT execute
和五一样不解析,<textarea>或<title>的内容中,唯一能够被解析器认做是标签的就是“</textarea>”或者“</title>”,在“<textarea>”和“<title>”的内容中不会创建标签,就不会有脚本能够执行
七:
<button οnclick="confirm('7');">Button</button>
<button onclick="confirm('7');">Button</button>
Character entity encoded '
Answer: The javascript will execute.
这里onclick
中为标签的属性值(类比2中的href
),会被HTML解码,得到
<button onclick="confirm('7');">Button</button>
然后被执行
八:
<button οnclick="confirm('8\u0027);">Button</button>
<button onclick="confirm('8\u0027);">Button</button>
Unicode escape sequence encoded '
Answer: The javascript will NOT execute.
onclick
中的值会交给JS处理,在JS中只有字符串和标识符能用Unicode表示,** '
**显然不行,JS执行失败
九:
<script>alert(9);</script>
<script>alert(9);</script>
Character entity encoded alert(9);
Answer: The javascript will NOT execute.
script
属于原始文本元素(Raw text elements),只可以容纳文本,注意没有字符引用,于是直接由JS处理,JS也认不出来,执行失败
原始文本元素(Raw text elements)有<script>
和<style>
十:
<script>\u0061\u006c\u0065\u0072\u0074(10);</script>
<script>\u0061\u006c\u0065\u0072\u0074(10);</script>
Unicode Escape sequence encoded alert
Answer: The javascript will execute.
同8,函数名alert
属于标识符,直接被JS执行
十一:
<script>\u0061\u006c\u0065\u0072\u0074\u0028\u0031\u0031\u0029</script>
<script>\u0061\u006c\u0065\u0072\u0074\u0028\u0031\u0031\u0029</script>
Unicode Escape sequence encoded alert(11)
Answer: The javascript will NOT execute.
编码了括号 ,同八
十二:
<script>\u0061\u006c\u0065\u0072\u0074(\u0031\u0032)</script>
<script>\u0061\u006c\u0065\u0072\u0074(\u0031\u0032)</script>
Unicode Escape sequence encoded alert and 12
Answer: The javascript will NOT execute.
十三:
<script>alert('13\u0027)</script>
<script>alert('13\u0027)</script>
Unicode escape sequence encoded '
Answer: The javascript will NOT execute.
这里看似将没毛病,但是这里\u0031\u0032
在解码的时候会被解码为字符串12
,注意是字符串,不是数字,文字显然是需要引号的,JS执行失败
十四:
<script>alert('14\u000a')</script>
<script>alert('14\u000a')</script>
Unicode escape sequence encoded line feed.
Answer: The javascript will execute.
\u000a
在JavaScript里是换行,就是\n
,直接执行
Java菜鸡才知道在Java里\u000a
是换行,相当于在源码里直接按一下回车键,后面的代码都换行了
十五:
<a href="javascript:%5c%75%30%30%36%31%5c%75%30%30%36%63%5c%75%30%30%36%35%5c%75%30%30%37%32%5c%75%30%30%37%34(15)"></a>
<a href="javascript:%5c%75%30%30%36%31%5c%75%30%30%36%63%5c%75%30%30%36%35%5c%75%30%30%37%32%5c%75%30%30%37%34(15)"></a>
Answer: The javascript will execute.
先HTML解码,得到
<a href="javascript:%5c%75%30%30%36%31%5c%75%30%30%36%63%5c%75%30%30%36%35%5c%75%30%30%37%32%5c%75%30%30%37%34(15)"></a>
在href中由URL模块处理,解码得到
javascript:\u0061\u006c\u0065\u0072\u0074(15)
识别JS协议,然后由JS模块处理,解码得到
javascript:alert(15)
最后被执行
总结:
-
<script>
和<style>
数据只能有文本,不会有HTML解码和URL解码操作 -
<textarea>
和<title>
里会有HTML解码操作,但不会有子元素 -
其他元素数据(如
div
)和元素属性数据(如href
)中会有HTML解码操作 -
部分属性(如
href
)会有URL解码操作,但URL中的协议需为ASCII -
JavaScript会对字符串和标识符Unicode解码
根据浏览器的自动解码,反向构造 XSS Payload 即可