社区微信群开通啦,扫一扫抢先加入社区官方微信群
社区微信群
(译者注:由于某些词汇翻译成中文后很生硬,因此把相应的英文标注在其后以便理解。这篇文章讲的内容很基础,同时也很重要,希望对大家有所帮助。)
这篇文章将要深入理解HTML、URL和JavaScript的规范细则和解析器,以及在解析一段XSS脚本时他们之间有着怎样的差别。这些内容对读者的难易程度取决于读者对HTML规范和浏览器解析的知识是否充足。当然,我向您保证这篇文章比较长,因此请准备一小时或两小时来从中获益。在主题开始之前,请花费一点时间来看看下列语句并尝试回答:这些脚本能够正确执行吗?
基础部分
1 2 3 4 5 6 7 8 9 10 11 | < a href = "%6a%61%76%61%73%63%72%69%70%74:%61%6c%65%72%74%28%31%29" ></ a > URL 编码 "javascript:alert(1)" < a href = "javascript:%61%6c%65%72%74%28%32%29" > HTML字符实体编码 "javascript" 和 URL 编码 "alert(2)" < a href = "javascript%3aalert(3)" ></ a > URL 编码 ":" < div ><img src=x οnerrοr=alert(4)></ div > HTML字符实体编码 < 和 > < textarea ><script>alert(5)</script></ textarea > HTML字符实体编码 < 和 > < textarea >< script >alert(6)</ script ></ textarea > |
高级部分
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | < button onclick = "confirm('7');" >Button</ button > HTML字符实体编码 " ' " (单引号) < button onclick = "confirm('8u0027);" >Button</ button > Unicode编码 " ' " (单引号) < script >alert(9);</ script > HTML字符实体编码 alert(9); < script >u0061u006cu0065u0072u0074(10);</ script > Unicode 编码 alert < script >u0061u006cu0065u0072u0074u0028u0031u0031u0029</ script > Unicode 编码 alert(11) < script >u0061u006cu0065u0072u0074(u0031u0032)</ script > Unicode 编码 alert 和 12 < script >alert('13u0027)</ script > Unicode 编码 " ' " (单引号) < script >alert('14u000a')</ script > Unicode 编码换行符(0x0A) |
额外赠送
1 | < 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 > |
这些问题的答案在这里,测试页面在这里。如果你能答对大部分问题并且没有感觉困惑,看到这就停下吧。如果没有,那么余下的内容将能够很好的帮助你理解浏览器解析过程。
在解析一篇HTML文档时主要有三个处理过程:HTML解析,URL解析和JavaScript解析。每个解析器负责解码和解析HTML文档中它所对应的部分,其工作原理已经在相应的解析器规范中明确写明。
0x01 HTML解析
从XSS的角度来说,我们感兴趣的是HTML文档是如何被词法解析的,因为我们并不想让用户提供的数据最终被解析为一段可执行脚本的script标签。HTML词法解析细则在这里。HTML词法解析细则是一篇冗长的文档,这篇博文并不会覆盖它的所有内容。这篇博文只会覆盖有关文档解码如何结束,以及新token何时被创建这两个有趣的部分。
一个HTML解析器作为一个状态机,它从输入流中获取字符并按照转换规则转换到另一种状态。在解析过程中,任何时候它只要遇到一个'<'符号(后面没有跟'/'符号)就会进入“标签开始状态(Tag open state)”。然后转变到“标签名状态(Tag name state)”,“前属性名状态(before attribute name state)”......最后进入“数据状态(Data state)”并释放当前标签的token。当解析器处于“数据状态(Data state)”时,它会继续解析,每当发现一个完整的标签,就会释放出一个token。
(译者注:词法解析是《编译原理》所涉及的内容,学习过编译原理的读者可以更好的理解“状态机”的工作原理)。
这里有三种情况可以容纳字符实体,“数据状态中的字符引用”,“RCDATA状态中的字符引用”和“属性值状态中的字符引用”。在这些状态中HTML字符实体将会从“&#...”形式解码,对应的解码字符会被放入数据缓冲区中。例如,在问题4中,“<”和“>”字符被编码为“<”和“>”。当解析器解析完“<div>”并处于“数据状态”时,这两个字符将会被解析。当解析器遇到“&”字符,它会知道这是“数据状态的字符引用”,因此会消耗一个字符引用(例如“<”)并释放出对应字符的token。在这个例子中,对应字符指的是“<”和“>”。读者可能会想:这是不是意味着“<”和“>”的token将会被理解为标签的开始和结束,然后其中的脚本会被执行?答案是脚本并不会被执行。原因是解析器在解析这个字符引用后不会转换到“标签开始状态”。正因为如此,就不会建立新标签。因此,我们能够利用字符实体编码这个行为来转义用户输入的数据从而确保用户输入的数据只能被解析成“数据”。
(译者注:这里要解释几个概念)
字符实体(character entities)
字符实体是一个转义序列,它定义了一般无法在文本内容中输入的单个字符或符号。一个字符实体以一个&符号开始,后面跟着一个预定义的实体的名称,或是一个#符号以及字符的十进制数字。
HTML字符实体(HTML character entities)
在HTML中,某些字符是预留的。例如在HTML中不能使用“<”或“>”,这是因为浏览器可能误认为它们是标签的开始或结束。如果希望正确地显示预留字符,就需要在HTML中使用对应的字符实体。一个HTML字符实体描述如下:
需要注意的是,某些字符没有实体名称,但可以有实体编号。
字符引用(character references)
字符引用包括“字符值引用”和“字符实体引用”。在上述HTML例子中,'<'对应的字符值引用为'<',对应的字符实体引用为‘<’。字符实体引用也被叫做“实体引用”或“实体”。)
现在你大概会明白为什么我们要转义“<”、“>”、“'” (单引号)和“"” (双引号)字符了。但为什么我们还要转义“&”呢?大概 “&” 是无辜的,任何跟在“&”后面的内容仅会被解释为字符引用,这并不会开始或闭合一个标签。事实上,“&”字符并不会打断HTML级别的转义过程,但它可能会打断其他级别的转义过程。我们将在JavaScript解析的部分讨论这个问题。
这里要提一下RCDATA的概念。要了解什么是RCDATA,我们先要了解另一个概念。在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段、其他元素和注释
版权声明:本文来源CSDN,感谢博主原创文章,遵循 CC 4.0 by-sa 版权协议,转载请附上原文出处链接和本声明。
原文链接:https://blog.csdn.net/qq_35513598/article/details/79861908
站方申明:本站部分内容来自社区用户分享,若涉及侵权,请联系站方删除。
如果觉得我的文章对您有用,请随意打赏。你的支持将鼓励我继续创作!