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

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

目录
相关文章
|
1月前
|
SQL 安全 网络安全
网络安全的护城河:漏洞防御与加密技术的深度解析
【10月更文挑战第37天】在数字时代的浪潮中,网络安全成为守护个人隐私与企业资产的坚固堡垒。本文将深入探讨网络安全的两大核心要素——安全漏洞和加密技术,以及如何通过提升安全意识来强化这道防线。文章旨在揭示网络攻防战的复杂性,并引导读者构建更为稳固的安全体系。
44 1
|
1月前
|
SQL 安全 测试技术
网络安全的盾牌与剑——漏洞防御与加密技术解析
【10月更文挑战第28天】 在数字时代的浪潮中,网络空间安全成为我们不可忽视的战场。本文将深入探讨网络安全的核心问题,包括常见的网络安全漏洞、先进的加密技术以及提升个人和组织的安全意识。通过实际案例分析和代码示例,我们将揭示黑客如何利用漏洞进行攻击,展示如何使用加密技术保护数据,并强调培养网络安全意识的重要性。让我们一同揭开网络安全的神秘面纱,为打造更加坚固的数字防线做好准备。
42 3
|
19天前
|
SQL 安全 算法
网络安全之盾:漏洞防御与加密技术解析
在数字时代的浪潮中,网络安全和信息安全成为维护个人隐私和企业资产的重要防线。本文将深入探讨网络安全的薄弱环节—漏洞,并分析如何通过加密技术来加固这道防线。文章还将分享提升安全意识的重要性,以预防潜在的网络威胁,确保数据的安全与隐私。
35 2
|
21天前
|
安全 算法 网络安全
网络安全的盾牌与剑:漏洞防御与加密技术深度解析
在数字信息的海洋中,网络安全是航行者不可或缺的指南针。本文将深入探讨网络安全的两大支柱——漏洞防御和加密技术,揭示它们如何共同构筑起信息时代的安全屏障。从最新的网络攻击手段到防御策略,再到加密技术的奥秘,我们将一起揭开网络安全的神秘面纱,理解其背后的科学原理,并掌握保护个人和企业数据的关键技能。
27 3
|
27天前
|
SQL 监控 安全
网络安全的盾牌与利剑:漏洞防御与加密技术解析
在数字时代的洪流中,网络安全如同一场没有硝烟的战争。本文将深入探讨网络安全的核心议题,从网络漏洞的发现到防御策略的实施,以及加密技术的运用,揭示保护信息安全的关键所在。通过实际案例分析,我们将一窥网络攻击的手段和防御的艺术,同时提升个人与企业的安全意识,共同构筑一道坚固的数字防线。
|
1月前
|
安全 算法 网络安全
网络安全的盾牌与剑:漏洞防御与加密技术解析
【10月更文挑战第42天】在数字时代的海洋中,网络安全是守护数据宝藏的坚固盾牌和锋利之剑。本文将揭示网络安全的两大支柱——漏洞防御和加密技术,通过深入浅出的方式,带你了解如何发现并堵塞安全漏洞,以及如何使用加密技术保护信息不被窃取。我们将一起探索网络安全的奥秘,让你成为信息时代的智者和守护者。
35 6
|
1月前
|
存储 SQL 安全
网络安全的屏障与钥匙:漏洞防御与加密技术解析
【10月更文挑战第42天】在数字时代的浪潮中,网络安全成为守护个人隐私与企业数据不被侵犯的关键防线。本文将深入探讨网络安全中的两大核心议题——漏洞防御和加密技术。我们将从网络漏洞的识别开始,逐步揭示如何通过有效的安全策略和技术手段来防范潜在的网络攻击。随后,文章将转向加密技术的奥秘,解读其在数据传输和存储过程中保护信息安全的作用机制。最后,强调提升个人和企业的安全意识,是构建坚固网络安全屏障的重要一环。
|
2月前
|
存储 安全 网络安全
网络安全的屏障与钥匙:漏洞防御与加密技术深度解析
【10月更文挑战第20天】在数字世界的迷宫中,网络安全是守护我们数据宝藏的坚固盾牌和锋利钥匙。本篇文章将带您穿梭于网络的缝隙之间,揭示那些潜藏的脆弱点—网络安全漏洞,同时探索如何通过现代加密技术加固我们的数字堡垒。从基本概念到实战策略,我们将一同揭开网络安全的神秘面纱,提升您的安全意识,保护个人信息不受侵犯。
57 25
|
1月前
|
安全 前端开发 Java
Web安全进阶:XSS与CSRF攻击防御策略深度解析
【10月更文挑战第26天】Web安全是现代软件开发的重要领域,本文深入探讨了XSS和CSRF两种常见攻击的原理及防御策略。针对XSS,介绍了输入验证与转义、使用CSP、WAF、HTTP-only Cookie和代码审查等方法。对于CSRF,提出了启用CSRF保护、设置CSRF Token、使用HTTPS、二次验证和用户教育等措施。通过这些策略,开发者可以构建更安全的Web应用。
93 4
|
1月前
|
安全 Go PHP
Web安全进阶:XSS与CSRF攻击防御策略深度解析
【10月更文挑战第27天】本文深入解析了Web安全中的XSS和CSRF攻击防御策略。针对XSS,介绍了输入验证与净化、内容安全策略(CSP)和HTTP头部安全配置;针对CSRF,提出了使用CSRF令牌、验证HTTP请求头、限制同源策略和双重提交Cookie等方法,帮助开发者有效保护网站和用户数据安全。
73 2

推荐镜像

更多
下一篇
DataWorks