从浏览器的解析规则认识XSS防御

本文涉及的产品
全局流量管理 GTM,标准版 1个月
公共DNS(含HTTPDNS解析),每月1000万次HTTP解析
云解析 DNS,旗舰版 1个月
简介: 从浏览器的解析规则认识XSS防御

文章目录
前言
浏览器解析
解析规则
字符编码
解析结果
转义实例
JS 转义
Html转义
自动转义
进阶示例
解析器原理
Html解析器
JS 解析器
URL解析器
解析顺序
总结
前言
作为 Web 漏洞家族常见的一员 —— XSS 跨站脚本攻击的身影依旧活跃在各类网站,而特殊字符转义作为 XSS 漏洞的主要修复和防御手段,被开发人员广泛应用。本文目的在于通过具体的代码实例,从浏览器的解析规则出发,分析 HTML 转义、JS 转义防御 XSS 攻击背后的原理,从而深入理解 XSS 漏洞。

浏览器解析
解析规则
浏览器在解析 HTML 文档时无论按照什么顺序,主要有三个过程:HTML 解析、JS 解析 和 URL 解析,每个解析器负责HTML文档中各自对应部分的解析工作。下面以一篇HTML文档解析来简单的讨论下解析器如何协同工作的。

首先浏览器接收到一个 HTML 文档时,会触发 HTML 解析器对 HTML 文档进行词法解析,这一过程完成 HTML解码 并创建 DOM 树;
接下来 JavaScript 解析器会介入对内联脚本进行解析,这一过程完成 JS 的解码工作;
如果浏览器遇到需要 URL 的上下文环境,这时 URL 解析器也会介入完成 URL 的解码工作。
URL 解析器的解码顺序会根据 URL 所在位置不同,导致在 JavaScript 解析器之前或之后解析。每个解析过程中也有许多细节,下面再做具体讨论。

字符编码
HTML字符实体:
在呈现 HTML 页面时,针对某些特殊字符如“<”或”>”直接使用,浏览器会误以为它们标签的开始或结束,若想正确的在 HTML 页面呈现特殊字符就需要用到其对应的字符实体。HTML 字符实体以& 开头 + 预先定义的实体名称,以分号结束,如“<”的实体名称为< 或以 & 开头+#符号以及字符的十进制数字(或者 十六 进制,都能解析),如”<”的实体编号为<。
JavaScript 编码:最常用的如 “\uXXXX” 这种写法为 Unicode 转义序列,表示一个字符,其中 xxxx 表示一个 16 进制数字,如”<” Unicode 编码为“\u003c”。
URL编码:%加字符的 ASCII 编码对于的 2 位 16 进制数字,如”/”对应的 URL 编码为%2f。
HTML 转义字符表 可以参考:
HTML在线编码转换 可以参考:

解析结果
简而言之,&#**;格式的字符串是 HTML 的转义字符,\是JS的转义符,转义的目的就是告诉解析器该符号为字符,而不是代码,防止代码出现歧义。

HTML 源代码 DOM 结构和浏览器解析后的 DOM 结构是不一样的:

在浏览器中我们右键查看网页源码(或者按住快捷键ctrl+u),看到的是服务器接受我们的请求后返回的 HTML 源码。
而按F12进入开发者工具面板,开发者工具分析出的 DOM 结构,就是浏览器的解析结果。
转义实例
JS 转义
来看一段测试代码 test1.php:














1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
将其部署在 phpstudy 的 WWW 文件夹下:

启动 phpstudy,访问测试页面:

当我们提交,前端并没有出现我们期待的弹窗,而是输出了以下字符串:

而当我们提交 提交后,得到的 html 源码如下:

当我们的 HTM L解析器解析到 。

这时它查找到是8行中的,而不是12行的。
这时和12行的之间的代码被当成字符串输出到前端页面。
而由于6行标签没有配对成功,故不会被浏览器解析为一个合法标签。 所以最终的解析结果是第8行的被解析为html标签。
故最终浏览器解析后的 DOM 结构如下:

当我们输入第二个 payload:。在到第8行时发现<\/script>标签,而不是,故继续往下,直到找寻到12行的标签,才完成了配对。
这时8行和11行的代码交给了 js 引擎去解析。 而这时又因为\为 js 语法中的转义字符,故在 js 解析引擎解析时,又能正常解析input_str变量的值为字符串,所以最总成功弹窗,很巧妙!
故最终浏览器解析后的 DOM 结构如下:
从上面实例我们可以具体了解到服务端返回的源码的 DOM 结构与浏览器解析后的 DOM 结构的差异,以及 JS 转义的巧妙作用!

Html转义
来看看另一段测试代码 test2.php:







<?php
if(isset($_POST['submit'])){
echo " str1";
echo "
";
echo "str2:".$_POST['str2'];
}
?>

str1:


str2:





1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
同样放在 phpstudy 网站目录下,浏览器访问:

我们将javascript:alert(1);进行 html 转义(字符的十六进制)得到如下字符串,并填写到 str1 输入框:

javascript:alert(1);
1
同时将进行 html 转义后得到如下字符,并填写到str2输入框:

<script>alert(1)</script>
1
提交后发现点击 str1 链接可以弹框,说明前者被当代码来执行了,而后者被当字符串输出了:

如何解释以上结果?

原理分析

提交 payload 之后,服务器返回的 html 源码代码如下:
而浏览器 html 解析器解析后的结果如下:
从上面可以看到,两个 payload 其实在浏览器的 HTML 解析器解析之后都被当成了字符输出了。

只是当用户再次点击 str1 的链接时,前者被解码之后的字符会被浏览器的 JS 解析器当成代码执行了。
而后者<script>alert(1);</script>被浏览器 html 解析器解码后已经被当作一个普通字符串而非 js 代码(请重点注意此时被浏览器解析后的结果是跟 str2:一起被包围在双引号里当作一个字符串显示),所以已无法被执行。
可以进一步来看看 str2 直接输入的话是什么效果,答案是会触发弹框:

进一步看看浏览器的解析结果:

此时,已经不是跟“str2:"一样被当作字符串输出了。

由上面的实例分析,可以清晰地看到 HTML 转义字符对于 XSS 防御的意义,它会告诉浏览器将一些危险字符当作普通字符串输出,而不是当作 js 代码执行。

自动转义
PHP 语言里,我们在服务端可以使用htmlspecialchars($str);函数对用户传输过来的客户端输入值进行 HTML 转义。

htmlspecialchars($str);函数只转换5个字符,即:和号(&),双引号(“),单引号(‘),小于(<),大于(>),将它们转换为 HTML 实体形式,输出时浏览器会自动还原并将其当成普通字符输出的。

利用htmlspecialchars($str);我们对前面 JS 转义的测试代码 test1.php 进行变更:













1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
此时再在浏览器输入 payload:这样字符实体并不会被解码,也就不会执行JS。

2、RCDATA

在HTML中,属于 RCDATA Elements 的标签有两个:textarea、title。RCDATA Elements 类型的标签可以包含文本内容和字符实体。解析器解析到 textarea、title 标签的数据部分时,状态会进入RCDATA State。前面我们提到,处于RCDATA State状态时,字符实体是会被解析器解码的。

示例:

<script>alert(5)</script>
<和>被编码为实体<和>。
解析器解析到它们时会进行解码,最终得到<script>alert(5)</script>。
但是里面的JS同样还是不会被执行,原因还是因为解码字符实体状态机不会进入标签打开状态(Tag Open State),
因此里面的
不能弹窗,Raw text elements类型标签下的所有字符实体编码都不会被HTML解码


能弹窗,在XML中,(会被解析成(,在XML中实体会自动转义,除了<[CDATA[和]]>包含的实体
1
2
3
4
JS 解析器
形如 \uXXXX这样的 Unicode 字符转义序列或 Hex 编码是否能被解码需要看情况。

首先,JavaScript 中有三个地方可以出现 Unicode 字符转义序列:

字符串中
Unicode 转义序列出现在字符串中时,它只会被解释为普通字符,而不会破坏字符串的上下文。例如,被编码转义的部分为10,是字符串,会被正常解码,JS代码也会被执行。
标识符中
若 Unicode 转义序列存在于标识符中,即变量名(如函数名等…),它会被进行解码。例如:,被编码转义的部分为 alert 字符,是函数名,属于在标识符中的情况,因此会被正常解码,JS代码也会被执行。
控制字符中
若 Unicode 转义序列存在于控制字符中,那么它会被解码但不会被解释为控制字符,而会被解释为标识符或字符串字符的一部分。控制字符即'、"、()等。例如,,其中(进行了Unicode编码,那么解码后它不再是作为控制字符,而是作为标识符的一部分alert(。因此函数的括号之类的控制字符进行 Unicode 转义后是不能被正常解释的。
总结,Unicode序列不能出现在控制字符中,否则不能被解释。

示例1:


被编码部分为alert(11)。
该例子中的JS不会被执行,因为控制字符被编码了。
1
2
3
示例2:


被编码部分为alert及括号内为12。
该例子中JS不会被执行,原因在于括号内被编码的部分不能被正常解释,要么使用ASCII数字,要么加""或''使其变为字符串,作为字符串也只能作为普通字符。
1
2
3
示例3:


被编码处为'。
该例的JS不会执行,因为控制字符被编码了,解码后的'将变为字符串的一部分,而不再解释为控制字符。
因此该例中字符串是不完整的,因为没有'来结束字符串。
1
2
3
4
示例4:


该例的JS会被执行,因为被编码的部分处于字符串内,只会被解释为普通字符,
不会突破字符串上下文。
1
2
3
示例5:


无法执行
我们以浏览器的视角来看:首先读到<开始读取标签,然后读到onerror调用JS解析器。在JS中,单引号,双引号和圆括号等属于控制字符,
编码后将无法识别。所以对于防御来说,应该编码这些控制字符。

下面这种方式可以解析:

可以结合上面的HTML编码
按照解析顺序反过去,先JS编码然后HTML解码

浏览器读到了<标签开始构造语法树,然后HTML解码,解码之后发现onerror于是进行一个JS解码,成功弹窗

延伸:
开发人员单纯的设置HTML实体编码为防御xss的手段,但是用户输入点在alert中

如果用户正常输入的话凡是存在< ," 等都能被转码
攻击者可以通过语句 ");alert("test,在服务端被转码:

弹窗两次,是因为浏览器进行HTML解码发现存在两个alert()

所以对于这种情况,正确防御XSS的方法:
应该是先JavaScript编码然后再进行HTML编码
用户输入 ");alert("test 后在服务端先JavaScript编码然后再进行HTML编码

在浏览器端:
首先经过第一步HTML解码后变为\u0022\u0029\u003B\u0061\u006C\u0065\u0072\u0074\u0028\u0022\u0074\u0065\u0073\u0074
JavaScript解析器工作,变为 ");alert("test ,刚才已经讲过JavaScript解析时只有标识符名称不会被当做字符串,
控制字符仅会被解析为标示符名称或者字符串,因此\u0022被解释成双引号文本,\u0028和\u0029被解释成为圆括号文本,不会变为控制字符被解析执行。
在这里采用的先JS编码后HTML编码中只弹窗了一次。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
URL解析器
通用URI的格式如下:

1
URL 解析器也被建模为状态机,文档输入流中的字符可以将其导向不同的状态。首先,要注意的是 URL 的 Scheme 部分(协议部分)必须为 ASCII 字符,即不能被任何编码,否则URL解析器的状态机将进入No Scheme状态。

示例:


URL编码部分的是javascript:alert(1)。
JS不会被执行,因为作为Scheme部分的"javascript"这个字符串被编码,导致URL解析器状态机进入No Scheme状态。
1
2
3
URL中的:也不能被以任何方式编码,否则URL解析器的状态机也将进入 No Scheme 状态。


由于:被URL编码为%3a,导致URL状态机进入No Scheme状态,JS代码不能执行。
1
2
示例:


"javascript"这个字符串被实体化编码,:没有被编码,alert(2)被URL编码。
成功执行
首先,在HTML解析器中我们谈到过,HTML状态机处于属性值状态(Attribute Value State)时,字符实体时会被解码的,此处在href属性中,所以被实体化编码的"javascript"字符串会被解码。其次,HTML解析是在URL解析之前的,所以在进行URL解析之前,Scheme部分的"javascript"字符串已被解码,而并不再是被实体编码的状态。
1
2
3
4
解析顺序
首先浏览器接收到一个 HTML 文档时,会触发 HTML 解析器对 HTML 文档进行词法解析,这一过程完成 HTML 解码并创建 DOM 树。
接下来 JavaScript 解析器会介入对内联脚本进行解析,这一过程完成J S的解码工作。
如果浏览器遇到需要 URL 的上下文环境,这时 URL 解析器也会介入完成 URL 的解码工作,URL 解析器的解码顺序会根据 URL 所在位置不同,可能在 JavaScript 解析器之前或之后解析。但HTML解析总是第一步。
URL 解析和 JavaScript 解析,它们的解析顺序要根据情况而定。

示例1:


该例子中,首先由HTML解析器对UserInput部分进行字符实体解码;
接着URL解析器对UserInput进行URL decode;
如果URL的Scheme部分为javascript的话,JavaScript解析器会再对UserInput进行解码。
所以解析顺序是:HTML解析->URL解析->JavaScript解析。
1
2
3
4
5
示例2:


该例子中,首先由HTML解析器对UserInput部分进行字符实体解码;
接着由JavaScript解析器会再对onclick部分的JS进行解析并执行JS;
执行JS后window.open('UserInput')函数的参数会传入URL,所以再由URL解析器对UserInput部分进行解码。
因此解析顺序为:HTML解析->JavaScript解析->URL解析。
1
2
3
4
5
示例3:


该例子中,首先还是由HTML解析器对UserInput部分进行字符实体解码;
接着由URL解析器解析href的属性值;
然后由于Scheme为javascript,所以由JavaScript解析;
解析执行JS后window.open('UserInput')函数传入URL,所以再由URL解析器解析。
所以解析顺序为:HTML解析->URL解析->JavaScript解析->URL解析。
1
2
3
4
5
6
综合实例:


1
2
3
4
5
6
7
8
9
10
11
首先 HTML 解析器进行解析,解析到 href 属性的值时,状态机进入属性值状态(Attribute Value State),该状态会解码字符实体。接着由URL解析器进行解析并解码,再接着由于 Scheme 为 javascript,因此由 JavaScript 解析器解析并解码,加上编码部分是函数名,属于标识符,因此可以正常解码解释。经过三轮解析解码后得到结果:。

总结
本文参考文章:

XSS与字符编码及浏览器解析原理;
XSS中的JS转义和HTML转义 ;
【应用安全——XSS】字符编码绕过;
深入探究浏览器编码及XSS Bypass ;
————————————————

                        版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。

原文链接:https://blog.csdn.net/weixin_39190897/article/details/113198859

目录
相关文章
|
11天前
|
安全 算法 网络安全
网络安全的盾牌与剑:漏洞防御与加密技术解析
【9月更文挑战第21天】在数字世界的海洋中,网络安全是航行者的罗盘和船锚。本文将揭开网络安全漏洞的面纱,探讨如何通过加密技术筑起数据保护的堡垒,并强调安全意识的重要性。从基础概念到实际操作,我们将一同航行在网络安全的波涛之中,寻找安全的灯塔。
|
11天前
|
安全 算法 网络安全
网络安全的盾牌与矛:漏洞防御与加密技术解析
【9月更文挑战第22天】在数字世界的棋盘上,网络安全是一场没有硝烟的战争。本文旨在揭示网络攻击的常见手段和防御策略,探讨如何通过提升安全意识和应用加密技术来保护信息安全。我们将从网络安全漏洞的类型、成因讲起,进而分析加密技术的原理与实践,最后强调培养良好的安全习惯的重要性。文章将用浅显的语言和生动的比喻,带领读者走进网络安全的世界,理解其复杂性并掌握基本防护知识。
|
8天前
|
存储 安全 JavaScript
XSS跨站脚本攻击详解(包括攻击方式和防御方式)
这篇文章详细解释了XSS跨站脚本攻击的概念、原理、特点、类型,并提供了攻击方式和防御方法。
25 1
|
11天前
|
SQL 安全 数据库
Python Web开发者必看!SQL注入、XSS、CSRF全面解析,守护你的网站安全!
在Python Web开发中,构建安全应用至关重要。本文通过问答形式,详细解析了三种常见Web安全威胁——SQL注入、XSS和CSRF,并提供了实用的防御策略及示例代码。针对SQL注入,建议使用参数化查询;对于XSS,需对输出进行HTML编码;而防范CSRF,则应利用CSRF令牌。通过这些措施,帮助开发者有效提升应用安全性,确保网站稳定运行。
26 1
|
15天前
|
监控 安全 网络安全
网络安全的盾与剑:漏洞防御与加密技术解析
【9月更文挑战第17天】在数字时代的浪潮中,网络安全成为保护数据和隐私的关键防线。本文深入浅出地探讨了网络安全的两大支柱:漏洞防御和加密技术,旨在提升公众的安全意识并分享防护策略。我们将从基础概念出发,逐步深入到技术细节,不仅阐释原理,还提供实际案例分析,帮助读者构建起一道坚固的数字防御墙。
33 3
|
21天前
|
SQL 安全 数据库
从入门到精通:Python Web安全守护指南,SQL注入、XSS、CSRF全防御!
【9月更文挑战第13天】在开发Python Web应用时,安全性至关重要。本文通过问答形式,详细介绍如何防范SQL注入、XSS及CSRF等常见威胁。通过使用参数化查询、HTML转义和CSRF令牌等技术,确保应用安全。附带示例代码,帮助读者从入门到精通Python Web安全。
45 6
|
23天前
|
存储 SQL 安全
网络安全的盾牌:漏洞防御与加密技术解析
【9月更文挑战第9天】在数字时代,网络安全的重要性日益凸显,它不仅是保护个人隐私和数据安全的屏障,也是维护社会稳定和经济繁荣的关键。本文将深入探讨网络安全中的漏洞防御策略、加密技术的运用以及提升公众安全意识的必要性,旨在通过知识分享,增强大众对网络威胁的防范能力,共同构建更安全的网络环境。
|
16天前
|
安全 网络安全 数据安全/隐私保护
网络安全的护城河:漏洞防御与加密技术解析
【9月更文挑战第16天】在数字信息的海洋中,网络安全是守护数据宝库的坚固城墙。本文将深入探讨网络安全中的漏洞防御和加密技术,揭示安全意识的重要性,并提供实用的代码示例,帮助读者构建起一道道防护墙,确保信息安全的堡垒坚不可摧。
31 0
|
2月前
|
安全 网络安全 数据安全/隐私保护
网络安全的护城河:漏洞防御与加密技术的深度解析
【8月更文挑战第31天】在数字化浪潮中,网络安全成为了保护个人隐私和企业资产的关键防线。本文通过浅显易懂的语言和实际代码示例,揭示网络安全漏洞的成因、加密技术的重要性以及提升安全意识的必要性。我们将从基础的网络攻击类型讲起,逐步深入到复杂的加密算法,旨在为不同层次的读者提供一份实用的网络安全指南。
|
2月前
|
安全 算法 网络安全
网络安全的盾牌与剑:漏洞防御与加密技术深度解析
【8月更文挑战第31天】在数字时代的海洋中,网络安全是航行者不可或缺的罗盘。本文将揭开网络安全漏洞的面纱,深入探索加密技术的奥秘,并通过代码示例揭示安全意识的重要性。从基础概念到实战应用,我们将一同航行在保护数据安全的航道上,确保每一位数字航海者的航程安全。

推荐镜像

更多
下一篇
无影云桌面