Web安全
基础漏洞
01前端基础【HTML】
02前端基础【CSS】
03后端基础【PHP速通】
04后端基础【PHP面向对象】
05MySQL基础操作
06前后端联动【代码练习】
07SQL注入【1】
07SQL 注入【2】
08SQL注入 Labs
08SQL注入速查表
09XSS
09跨站脚本攻击【XSS】
09XSS Labs
10跨站请求伪造【CSRF】
11服务器端请求伪造【SSRF】
12XML 外部实体注入【XXE】
13代码执行漏洞
14命令执行漏洞
15文件包含漏洞
16文件上传漏洞
17反序列化漏洞
18业务逻辑漏洞
19未授权访问漏洞集合
20跨源资源共享【CORS】
21SSTI模板注入
22并发漏洞
23点击劫持【Clickjacking 】
24请求走私
25路径遍历
26访问控制
27身份验证漏洞
28WebSocket
29Web缓存中毒
30HTTP 主机头攻击
31信息泄露漏洞
32原型污染
33NoSQL注入
API 安全
01web应用程序
02HTTP协议
03API概述
04分类类型
05交换格式
06身份验证
07常见API漏洞
08crAPI靶场
09JWT
10OAuth 2.0身份验证
11GraphQL【1】
11GraphQL【2】
12DVGA靶场
13服务器端参数污染
14API文档
15API Labs
16OAuth Labs
17GraphQL API Labs
18JWT Labs
小程序
小程序抓包
数据库
MySQL
Oracle
MongoDB
Redis
PostgreSQL
SQL server
中间件
Nginx
Apache HTTP Server
IIS
Tomcat
框架
ThinkPHP
Spring
Spring Boot
Django
访问控制
-
+
首页
09XSS
## 一、定义 Cross-Site Scripting,跨站脚本,由于在前端有一个CSS 层叠样式表,所以跨站脚本简写为XSS。 攻击者可以利用该漏洞入侵用户与存在漏洞的应用程序之间的交互。它允许攻击者规避同源策略,该策略旨在隔离不同的网站。跨站脚本漏洞通常允许攻击者伪装成受害者用户,执行用户可以执行的任何操作,并访问用户的任何数据。如果受害者用户在应用程序中拥有特权访问权限,那么攻击者可能能够完全控制应用程序的所有功能和数据。 ## 二、工作模式 跨站脚本攻击的工作原理是操纵易受攻击的网站,使其向用户返回恶意 JavaScript。当恶意代码在受害者的浏览器中执行时,攻击者可以完全破坏其与应用程序的交互。 ## 三、类型 XSS 攻击主要有三种类型,分别是: - 反射型 XSS,其中恶意脚本来自当前 HTTP 请求。 - 存储型 XSS,其中恶意脚本来自网站的数据库。 - 基于 DOM 的 XSS,其中漏洞存在于客户端代码而不是服务器端代码中。 ### 3.1 反射型 反射型 XSS 是跨站脚本攻击中最简单的一种。当应用程序接收 HTTP 请求中的数据,并以不安全的方式将该数据包含在即时响应中时,就会发生反射型 XSS。 假设某个网站具有搜索功能,该功能在 URL 参数中接收用户提供的搜索词: ```http https://insecure-website.com/search?term=gift ``` 应用程序在对以下 URL 的响应中回显所提供的搜索词: ```http <p>You searched for: gift</p> ``` 假设应用程序不对数据执行任何其他处理,攻击者可以构建如下攻击: ```http https://insecure-website.com/search?term=<script> alert(2)</script> ``` 此 URL 产生的响应就是弹窗2。 如果应用程序的另一个用户请求攻击者的 URL,那么攻击者提供的脚本将在受害者用户的浏览器中,在他们与应用程序的会话上下文中执行。 > 影响 如果攻击者能够控制受害者浏览器中执行的脚本,那么他们通常就能完全攻陷该用户。除此之外,攻击者还可以: - 执行应用程序内用户可以执行的任何操作。 - 查看用户能够查看的任何信息。 - 修改用户能够修改的任何信息。 - 发起与其他应用程序用户的交互,包括恶意攻击,这些交互看起来源自最初的受害用户。 攻击者可能会使用多种手段诱使受害用户发出他们控制的请求,从而发动反射型 XSS 攻击。这些手段包括将链接放置在攻击者控制的网站上,或放置在允许生成内容的其他网站上,或者通过电子邮件、推文或其他消息发送链接。此类攻击可能直接针对已知用户,也可能是对应用程序的任何用户进行无差别攻击。 由于攻击需要外部传递机制,因此反射型 XSS 的影响通常不如存储型 XSS 严重,因为自包含攻击可以在易受攻击的应用程序本身内传递。 > 查找和测试反射型 XSS 漏洞 手动测试反射型 XSS 漏洞涉及以下步骤: **测试每个入口点。**分别测试应用程序 HTTP 请求中每个入口点的数据。这包括 URL 查询字符串和消息正文中的参数或其他数据,以及 URL 文件路径。测试还包括 HTTP 标头,尽管只有通过某些 HTTP 标头才能触发的类似 XSS 的行为在实践中可能无法被利用。 提交随机字母数字值。对于每个入口点,提交一个唯一的随机值,并确定该值是否反映在响应中。 **确定反射上下文。**对于响应中每个反射随机值的位置,确定其上下文。这可能是 HTML 标签之间的文本、可能被引用的标签属性内、JavaScript 字符串内等等。 **测试候选有效载荷。**根据反射的上下文,测试初始候选 XSS 有效载荷,如果它在响应中未经修改地反射,则会触发 JavaScript 执行。测试有效载荷的最简单方法是将请求发送到Burp Repeater,修改请求以插入候选有效载荷,发出请求,然后检查响应以查看有效载荷是否有效。另一种有效的工作方法是保留请求中的原始随机值,并将候选 XSS 有效载荷放在其之前或之后。然后在 Burp Repeater 的响应视图中将随机值设置为搜索词。Burp 将突出显示搜索词出现的每个位置,快速找到反射。 **测试替代有效载荷。**如果候选 XSS 有效载荷已被应用程序修改或完全阻止,则您需要测试可能根据反射上下文和正在执行的输入验证类型发起有效 XSS 攻击的替代有效载荷和技术。 > 反射型XSS与self-XSS的区别 self-XSS涉及的应用程序行为与常规反射型 XSS 类似,但它无法通过精心设计的 URL 或跨域请求以常规方式触发。相反,只有当受害者自己从浏览器提交 XSS 负载时,才会触发此漏洞。发起self-XSS 攻击通常需要对受害者进行社会工程攻击,使其将攻击者提供的一些输入粘贴到浏览器中。因此,它通常被认为是一个影响较小的问题。 ### 3.2 存储型XSS 当应用程序从不受信任的来源接收数据并以不安全的方式将该数据包含在其后续 HTTP 响应中时,就会出现存储型跨站点脚本(也称为二次或持久性 XSS)。 假设某个网站允许用户对博客文章发表评论,并向其他用户展示。用户使用如下 HTTP 请求提交评论: ```HTTP POST /post/comment HTTP/1.1 Host: vulnerable-website.com Content-Length: 100 postId=3&comment=This+post+was+extremely+helpful.&name=Carlos+Montoya&email=carlos%40normal-user.net ``` 提交此评论后,任何访问该博客文章的用户都将在应用程序的回复中收到以下内容: ```txt <p>This post was extremely helpful.</p> ``` 假设应用程序不对数据进行任何其他处理,攻击者可以提交如下恶意评论: ```txt <script>/* Bad stuff here... */</script> ``` 在攻击者的请求中,此评论将被 URL 编码为: ```http comment=%3Cscript%3E%2F*%2BBad%2Bstuff%2Bhere...%2B*%2F%3C%2Fscript%3E ``` 任何访问该博客文章的用户现在都将在应用程序的响应中收到以下内容: ```txt <p><script>/* Bad stuff here... */</script></p> ``` 攻击者提供的脚本将在受害者用户的浏览器中,在他们与应用程序的会话环境中执行。 > 影响 如果攻击者能够控制受害者浏览器中执行的脚本,那么他们通常就能完全攻陷该用户。攻击者可以执行任何与反射型XSS漏洞 影响相关的操作。 就可利用性而言,反射型 XSS 和存储型 XSS 之间的关键区别在于,存储型 XSS 漏洞允许在应用程序本身内发起攻击。攻击者无需寻找外部途径来诱导其他用户发出包含其漏洞利用代码的特定请求。相反,攻击者将漏洞利用代码植入应用程序本身,只需等待用户发现即可。 存储型跨站脚本攻击的独立性在 XSS 漏洞仅影响当前登录应用程序的用户的情况下尤为重要。如果 XSS 是反射型的,则攻击的时机必须恰到好处:如果用户在未登录的情况下被诱导发出攻击者的请求,则不会受到攻击。相反,如果 XSS 是存储型的,则可以保证用户在遇到攻击时处于登录状态。 > 查找和测试存储型 XSS 漏洞 需要测试所有相关的“入口点”,攻击者可控的数据可以通过这些入口点进入应用程序的处理过程,以及所有可能在应用程序响应中出现该数据的“出口点”。 应用程序处理的入口点包括: - URL 查询字符串和消息正文中的参数或其他数据。 - URL 文件路径。 - 与反射型 XSS 相关的 HTTP 请求标头可能无法被利用。 - 任何攻击者可以通过带外路由将数据传递到应用程序中,存在的路由完全取决于应用程序实现的功能。 存储型 XSS 攻击的出口点是所有可能的 HTTP 响应,这些响应在任何情况下返回给任何类型的应用程序用户。 测试存储型XSS漏洞的第一步是找到入口点和出口点之间的连接,即提交到入口点的数据会从出口点发出。这之所以具有挑战性,是因为: - 原则上,提交到任何入口点的数据都可以从任何出口点发出。例如,用户提供的显示名称可能会出现在只有部分应用程序用户可见的模糊审计日志中。 - 应用程序当前存储的数据通常很容易因应用程序内执行的其他操作而被覆盖。例如,搜索功能可能会显示最近搜索的列表,而这些列表很快就会随着用户执行其他搜索而被覆盖。 ### 3.3 DOM型XSS DOM-based XSS 基于 DOM型 的 XSS 漏洞通常发生在 JavaScript 从攻击者可控制的源(例如 URL)获取数据,并将其传递给支持动态代码执行的接收器(例如eval()或 innerHTML时),这使得攻击者能够执行恶意 JavaScript,这通常允许他们劫持其他用户的帐户。 要发起基于 DOM型 的 XSS 攻击,您需要将数据放入源中,以便将其传播到接收器并导致执行任意 JavaScript。 DOM XSS 最常见的来源是 URL,通常通过window.location对象访问。攻击者可以构建一个链接,将受害者引导至一个易受攻击的页面,并在查询字符串和 URL 的片段部分中嵌入有效载荷。在某些情况下,例如针对 404 页面或运行 PHP 的网站时,有效载荷也可以放置在路径中。 > 测试DOM型XSS **测试 HTML 接收器** 要在 HTML 接收器中测试 DOM型 XSS,请将随机的字母数字字符串放入源文件(例如location.search),然后使用开发者工具检查 HTML 并找到该字符串出现的位置。请注意,浏览器的“查看源代码”选项不适用于 DOM XSS 测试,因为它不考虑 JavaScript 对 HTML 执行的更改。在 Chrome 的开发者工具中,您可以使用Control+F(或Command+F在 MacOS 上)在 DOM 中搜索该字符串。 对于字符串在 DOM 中出现的每个位置,您都需要识别上下文。基于此上下文,您需要优化输入以了解其处理方式。例如,如果您的字符串出现在双引号属性中,则尝试在字符串中注入双引号,看看能否突破该属性的限制。 请注意,不同浏览器在 URL 编码方面的行为有所不同。Chrome、Firefox 和 Safari 会对 和 进行 URL 编码location.search,location.hash而 IE11 和 Microsoft Edge(Chromium 之前的版本)则不会对这些来源进行 URL 编码。如果您的数据在处理之前就进行了 URL 编码,那么 XSS 攻击不太可能奏效。 **测试 JavaScript 执行接收器** 测试 JavaScript 执行接收器是否包含基于 DOM 的 XSS 漏洞会稍微困难一些。由于这些接收器的输入不一定会出现在 DOM 中的任何位置,因此你无法搜索它。你需要使用 JavaScript 调试器来确定你的输入是否以及如何发送到接收器。 对于每个潜在来源(例如location),您首先需要在页面的 JavaScript 代码中查找引用该来源的情况。在 Chrome 开发者工具中,您可以使用Control+Shift+F(Command+Alt+F在 MacOS 上为 )在页面的所有 JavaScript 代码中搜索该来源。 找到读取源的位置后,您可以使用 JavaScript 调试器添加断点,并跟踪源值的使用方式。您可能会发现源被赋值给了其他变量。如果是这种情况,您需要再次使用搜索功能来跟踪这些变量,看看它们是否被传递给了接收器。当您发现接收器被赋值了来自源的数据时,您可以使用调试器检查该值,方法是将鼠标悬停在变量上,以在值发送到接收器之前显示其值。然后,与 HTML 接收器一样,您需要优化输入,看看是否可以成功发起 XSS 攻击。 在野外识别和利用 DOM XSS 可能是一个繁琐的过程,通常需要手动搜索复杂且精简的 JavaScript。 原则上,如果存在一条可执行路径,允许数据从源传播到接收器,则网站容易受到基于 DOM 的跨站脚本攻击。实际上,不同的源和接收器具有不同的属性和行为,这些属性和行为会影响可利用性,并决定需要哪些技术。此外,网站的脚本可能会执行验证或其他数据处理,这些操作在尝试利用漏洞时必须进行。有多种接收器与基于 DOM 的漏洞相关。 > 哪些接收器可能导致 DOM型-XSS 漏洞 ```txt document.write() document.writeln() document.domain element.innerHTML element.outerHTML element.insertAdjacentHTML element.onevent ``` > jQuery 函数也是可能导致 DOM-XSS 漏洞的接收器 ```txt add() after() append() animate() insertAfter() insertBefore() before() html() prepend() replaceAll() replaceWith() wrap() wrapInner() wrapAll() has() constructor() init() index() jQuery.parseHTML() $.parseHTML() ``` > 如何避免DOM型XSS 避免允许来自任何不受信任来源的数据动态写入 HTML 文档。 ## 四、跨站点脚本上下文 在测试反射型和存储型XSS 时,一项关键任务是识别 XSS 上下文: - 响应中攻击者可控制的数据出现的位置; - 应用程序对该数据执行的任何输入验证或其他处理。 ### 4.1 HTML标签之间的XSS 当 XSS 上下文是 HTML 标签之间的文本时,需要引入一些旨在触发 JavaScript 执行的新 HTML 标签。 执行 JavaScript 的一些有用方法是: ```javascript <script>alert(document.domain)</script> <img src=1 onerror=alert(1)> ``` ### 4.2 HTML 标签属性中的 XSS 当 XSS 上下文位于 HTML 标签属性值中时,有时您可以终止该属性值、关闭标签,然后引入新的属性值。例如: ```javascript "><script>alert(document.domain)</script> ``` 在这种情况下,尖括号会被屏蔽或编码,因此您的输入无法跳出其所在的标签。如果您可以终止属性值,通常可以引入一个可以创建脚本化上下文的新属性,例如事件处理程序。例如: ```javascript " autofocus onfocus=alert(document.domain) x=" ``` 上述有效载荷创建了一个`onfocus`事件,该事件将在元素获得焦点时执行 JavaScript,同时还添加了`autofocus`属性,以尝试在`onfocus`无需任何用户交互的情况下自动触发该事件。最后,它添加了`x="`以下标记以优雅地修复。 ### 4.3 JavaScript中的XSS 当 XSS 上下文是响应中一些现有的 JavaScript 时,可能会出现各种各样的情况,需要不同的技术才能成功利用。 **终止现有脚本** 最简单的情况是,可以简单地关闭包含现有 JavaScript 的脚本标签,并引入一些将触发 JavaScript 执行的新 HTML 标签。例如,如果 XSS 上下文如下: ```javascript <script> ... var input = 'controllable data here'; ... </script> ``` 那么您可以使用以下有效载荷来突破现有的 JavaScript 并执行您自己的: ```javascript </script><img src=1 onerror=alert(document.domain)> ``` 之所以有效,是因为浏览器首先会执行 HTML 解析来识别包含脚本块的页面元素,之后才会执行 JavaScript 解析来理解和执行嵌入的脚本。上述有效载荷导致原始脚本损坏,并带有未终止的字符串字面量。但这并不妨碍后续脚本以正常方式被解析和执行。 **打破 JavaScript 字符串** 如果 XSS 上下文位于带引号的字符串文字内,通常可以跳出字符串并直接执行 JavaScript。修复 XSS 上下文之后的脚本至关重要,因为那里的任何语法错误都会导致整个脚本无法执行。 一些有用的脱离字符串文字的方法如下: ``` '-alert(document.domain)-' ';alert(document.domain)// ``` **反斜杠转义** 某些应用程序会尝试使用反斜杠转义所有单引号字符,以防止输入脱离 JavaScript 字符串。 字符前的反斜杠会告知 JavaScript 解析器该字符应按字面意思解释,而不是像字符串终止符这样的特殊字符。在这种情况下,应用程序经常会犯一个错误,那就是没有对反斜杠字符本身进行转义。这意味着攻击者可以使用自己的反斜杠字符来抵消应用程序添加的反斜杠。 例如,假设输入: ```javascript ';alert(document.domain)// ``` 转换为: ```javascript \';alert(document.domain)// ``` 您现在可以使用替代有效载荷: ```javascript \';alert(document.domain)// ``` 转换为: ```javascript \\';alert(document.domain)// ``` 这里,第一个反斜杠意味着第二个反斜杠会被解释为字面意思,而不是特殊字符。这意味着引号现在被解释为字符串终止符,因此攻击成功。 **冷门函数** 一些网站通过限制您可以使用的字符来使 XSS 更加困难。这可以在网站级别进行,也可以通过部署 WAF 来阻止您的请求到达网站。在这些情况下,您需要尝试其他调用函数的方式来绕过这些安全措施。一种方法是使用throw带有异常处理程序的语句。这使您无需使用括号即可将参数传递给函数。以下代码将alert()函数分配给全局异常处理程序,并且throw语句将传递1给异常处理程序(在本例中为alert)。最终结果是使用作为参数 alert()调用该函数。 ```txt onerror=alert;throw 1 ``` 有多种方法可以使用此技术来调用不带括号的函数。 ### 4.4 使用 HTML 编码 当 XSS 上下文是引用标签属性内的一些现有 JavaScript(例如事件处理程序)时,可以利用 HTML 编码来绕过一些输入过滤器。 当浏览器解析出响应中的 HTML 标签和属性后,它会先对标签属性值进行 HTML 解码,然后再进行进一步处理。如果服务器端应用程序阻止或过滤了 XSS 攻击所需的某些字符,通常可以通过对这些字符进行 HTML 编码来绕过输入验证。 例如,如果 XSS 上下文如下: ```txt <a href="#" onclick="... var input='controllable data here'; ..."> ``` 并且应用程序阻止或转义单引号字符,您可以使用以下有效负载来突破 JavaScript 字符串并执行您自己的脚本: ```txt '-alert(document.domain)-' ``` 该`'`序列是一个表示撇号或单引号的 HTML 实体。由于浏览器onclick在解释 JavaScript 代码之前会先对属性值进行 HTML 解码,因此该实体会被解码为引号,并成为字符串分隔符,从而导致攻击成功。 ### 4.5 JavaScript 模板文字中的 XSS JavaScript 模板字面量是允许嵌入 JavaScript 表达式的字符串字面量。嵌入的表达式会被求值,并通常连接到周围的文本中。模板字面量用反引号(而不是普通的引号)封装,并使用${...}语法来标识嵌入的表达式。 例如,以下脚本将打印包含用户显示名称的欢迎消息: ```txt document.getElementById('message').innerText = `Welcome, ${user.displayName}.`; ``` 当 XSS 上下文位于 JavaScript 模板字面量中时,无需终止该字面量。您只需使用`${...}`语法嵌入一个 JavaScript 表达式,该表达式将在处理该字面量时执行。例如,如果 XSS 上下文如下所示: ```txt <script> ... var input = `controllable data here`; ... </script> ``` 那么您可以使用以下有效负载来执行 JavaScript,而无需终止模板文字: ```txt ${alert(document.domain)} ``` ## 五、XSS的利用 发现了跨站脚本漏洞的传统方法是使用alert()函数创建一个弹出窗口。 这并不是因为 XSS 与弹出窗口有任何关联,它只是一种证明您可以在给定域名上执行任意 JavaScript 的方法。您可能会注意到有些人使用`alert(document.domain)`。这是一种明确 JavaScript 在哪个域名上执行的方法。 有时,可能希望更进一步,通过提供完整的漏洞利用代码来证明 XSS 漏洞确实存在威胁。 ### 5.1 利用跨站脚本窃取cookie 窃取 Cookie 是利用 XSS 的传统方法。大多数 Web 应用程序都使用 Cookie 来处理会话。您可以利用跨站脚本漏洞,将受害者的 Cookie 发送到您自己的域,然后手动将 Cookie 注入浏览器并冒充受害者。 在实践中,这种方法有一些明显的局限性: - 受害者可能尚未登录。 - 许多应用程序使用该标志来向 JavaScript 隐藏其 cookie `HttpOnly`。 - 会话可能会被锁定到其他因素,例如用户的 IP 地址。 - 在您能够劫持会话之前,会话可能会超时。 ### 5.2 利用跨站点脚本获取密码 如今,许多用户都使用密码管理器自动填充密码。您可以利用此功能,创建一个密码输入框,读取自动填充的密码,并将其发送到您自己的域名。这种技术可以避免大多数与窃取 Cookie 相关的问题,甚至可以访问受害者重复使用相同密码的所有其他帐户。 这种技术的主要缺点是,它只对拥有密码管理器并支持密码自动填充功能的用户有效。(当然,如果用户没有保存密码,你仍然可以尝试通过站内钓鱼攻击获取他们的密码,但效果并不完全相同。) ### 5.3 利用跨站脚本绕过 CSRF 保护 XSS 使攻击者能够执行合法用户在网站上可以执行的几乎所有操作。通过在受害者的浏览器中执行任意 JavaScript,XSS 允许您像受害者用户一样执行各种操作。例如,您可以让受害者发送消息、接受好友请求、向源代码存储库提交后门或转移一些比特币。 有些网站允许已登录用户更改其电子邮件地址,而无需重新输入密码。如果您在其中一个网站上发现 XSS 漏洞,则可以利用它来窃取 CSRF 令牌。使用该令牌,您可以将受害者的电子邮件地址更改为您控制的地址。然后,您可以触发密码重置以获取该帐户的访问权限。 这种类型的漏洞利用将 XSS(窃取 CSRF 令牌)与 CSRF 通常针对的功能相结合。传统的 CSRF 是一种“单向”漏洞,攻击者可以诱导受害者发送请求,但无法查看响应,而 XSS 则支持“双向”通信。这使得攻击者既可以发送任意请求,又可以读取响应,从而形成一种能够绕过反 CSRF 防御措施的混合攻击。 ## 六、如何防护XSS XSS预防通常可以通过两层防御来实现: - 对输出数据进行编码; - 到达时验证输入。 ### 6.1 对输出数据进行编码 编码应该在用户可控制的数据写入页面之前直接应用,因为写入的上下文决定了需要使用哪种编码。例如,JavaScript 字符串中的值需要的转义类型与 HTML 上下文中的值不同。 在 HTML 上下文中,您应该将非白名单值转换为 HTML 实体: - `<`转换为:`<` - `>`转换为:`>` 在 JavaScript 字符串上下文中,非字母数字值应该进行 Unicode 转义: - `<`转换为:`\u003c` - `>`转换为:`\u003e` 有时你需要按正确的顺序应用多层编码。例如,为了安全地将用户输入嵌入到事件处理程序中,你需要同时处理 JavaScript 上下文和 HTML 上下文。因此,你需要先对输入进行 Unicode 转义,然后再进行 HTML 编码: ```txt <a href="#" onclick="x='This string needs two layers of escaping'">test</a> ``` ### 6.2 到达时验证输入 编码可能是 XSS 防御中最重要的一道防线,但它不足以在所有情况下都杜绝 XSS 漏洞。您还应该在首次收到用户输入时尽可能严格地验证输入。 输入验证的示例包括: - 如果用户提交的 URL 将在响应中返回,请验证该 URL 是否以安全协议(例如 HTTP 和 HTTPS)开头。否则,有人可能会使用`javascript`或 等有害协议来攻击您的网站`data`。 - 如果用户提供一个预期为数字的值,则验证该值是否确实包含整数。 - 验证输入仅包含预期的一组字符。 理想情况下,输入验证应该通过阻止无效输入来实现。另一种方法是尝试清除无效输入,使其变为有效输入,这种方法更容易出错,应尽可能避免。 ### 6.3 白名单与黑名单 输入验证通常应使用白名单而非黑名单。例如,与其尝试列出所有有害协议(`javascript`例如`data`、 等),不如简单地列出安全协议(例如 HTTP、HTTPS),并禁止列表中未列出的任何协议。 这可以确保您的防御系统在出现新的有害协议时不会失效,并降低其受到试图混淆无效值以逃避黑名单的攻击的可能性。 ### 6.4 允许“安全” HTML 应尽可能避免允许用户发布 HTML 标记,但有时这是业务需求。例如,博客网站可能允许发布包含有限 HTML 标记的评论。 经典的方法是尝试过滤掉潜在的有害标签和 JavaScript。您可以尝试使用安全标签和属性的白名单来实现这一点,但由于浏览器解析引擎的差异以及突变 XSS 等怪异行为,这种方法很难安全地实现。 最不糟糕的选择是使用在用户浏览器中执行过滤和编码的 JavaScript 库,例如 DOMPurify。其他库允许用户以 Markdown 格式提供内容,并将 Markdown 转换为 HTML。遗憾的是,所有这些库都可能存在 XSS 漏洞,因此这并非完美的解决方案。如果您确实使用了其中一种库,则应密切关注其安全更新。 ### 6.5 使用模板引擎防止XSS 许多现代网站使用服务器端模板引擎(例如 Twig 和 Freemarker)在 HTML 中嵌入动态内容。这些引擎通常定义自己的转义系统。例如,在 Twig 中,你可以使用`e()`过滤器,并附带一个定义上下文的参数: ``` {{ user.firstname | e('html') }} ``` 一些其他模板引擎,例如 Jinja 和 React,默认转义动态内容,从而有效地防止大多数 XSS 的发生。 ### 6.6 在 PHP 中防止 XSS PHP 中有一个内置函数用于对实体进行编码,名为`htmlentities`。在 HTML 上下文中,您应该调用此函数来转义您的输入。调用此函数时,需要使用三个参数: - 您的输入字符串。 - `ENT_QUOTES`,这是一个指定所有引号都应进行编码的标志。 - 字符集,大多数情况下应该是UTF-8。 例如: ``` <?php echo htmlentities($input, ENT_QUOTES, 'UTF-8');?> ``` 在 JavaScript 字符串上下文中,您需要对输入进行 Unicode 转义,正如上文所述。遗憾的是,PHP 不提供对字符串进行 Unicode 转义的 API。以下是一些在 PHP 中执行此操作的代码: ```php <?php function jsEscape($str) { $output = ''; $str = str_split($str); for($i=0;$i<count($str);$i++) { $chrNum = ord($str[$i]); $chr = $str[$i]; if($chrNum === 226) { if(isset($str[$i+1]) && ord($str[$i+1]) === 128) { if(isset($str[$i+2]) && ord($str[$i+2]) === 168) { $output .= '\u2028'; $i += 2; continue; } if(isset($str[$i+2]) && ord($str[$i+2]) === 169) { $output .= '\u2029'; $i += 2; continue; } } } switch($chr) { case "'": case '"': case "\n"; case "\r"; case "&"; case "\\"; case "<": case ">": $output .= sprintf("\\u%04x", $chrNum); break; default: $output .= $str[$i]; break; } } return $output; } ?> ``` 以下是在 PHP 中使用该函数的方法`jsEscape`: ```txt <script>x = '<?php echo jsEscape($_GET['x'])?>';</script> ``` 或者,可以使用模板引擎。 ### 6.7 在 JavaScript 中防止 XSS 客户端 在 JavaScript 的 HTML 上下文中转义用户输入,您需要自定义 HTML 编码器,因为 JavaScript 不提供用于编码 HTML 的 API。以下是一些将字符串转换为 HTML 实体的 JavaScript 代码示例: ```javascript function htmlEncode(str){ return String(str).replace(/[^\w. ]/gi, function(c){ return '&#'+c.charCodeAt(0)+';'; }); } ``` 然后您可以按如下方式使用该函数: ```txt <script>document.body.innerHTML = htmlEncode(untrustedValue)</script> ``` 如果您的输入是 JavaScript 字符串,则需要一个执行 Unicode 转义的编码器。以下是一个 Unicode 编码器的示例: ```txt function jsEscape(str){ return String(str).replace(/[^\w. ]/gi, function(c){ return '\\u'+('0000'+c.charCodeAt(0).toString(16)).slice(-4); }); } ``` 然后您可以按如下方式使用该函数: ```txt <script>document.write('<script>x="'+jsEscape(untrustedValue)+'";<\/script>')</script> ``` ### 6.8 在 jQuery 中防止 XSS jQuery 中最常见的 XSS 形式是将用户输入传递给 jQuery 选择器。 Web 开发者经常会使用输入`location.hash`并将其传递给选择器,这会导致 XSS,因为 jQuery 会渲染 HTML。 jQuery 意识到了这个问题,并修补了其选择器逻辑,以检查输入是否以哈希开头。 现在,只有当第一个字符为 时,jQuery 才会渲染 HTML `<`。如果您将不受信任的数据传递给 jQuery 选择器,请确保使用上述函数正确地对值进行转义`jsEscape`。 ### 6.9 使用内容安全策略(CSP)缓解 XSS 内容安全策略 (CSP) 是抵御跨站脚本攻击的最后一道防线。如果您的 XSS 防御措施失效,您可以使用 CSP 来限制攻击者的操作,从而缓解 XSS 攻击。 CSP 允许您控制各种功能,例如是否可以加载外部脚本以及是否执行内联脚本。要部署 CSP,您需要添加一个 HTTP 响应标头,该标头的调用`Content-Security-Policy`值包含您的策略。 CSP 示例如下: ```txt default-src 'self'; script-src 'self'; object-src 'none'; frame-src 'none'; base-uri 'none'; ``` 此策略规定,图片和脚本等资源只能从与主页面相同的源加载。因此,即使攻击者成功注入 XSS 负载,他们也只能从当前源加载资源。这大大降低了攻击者利用 XSS 漏洞的可能性。 如果您需要加载外部资源,请确保只允许那些不会帮助攻击者利用您网站的脚本。例如,如果您将某些域名列入白名单,那么攻击者就可以加载来自这些域名的任何脚本。尽可能尝试将资源托管在您自己的域名上。 如果无法做到这一点,那么您可以使用基于哈希值或随机数 (nonce) 的策略来允许不同域上的脚本。随机数是一个随机字符串,作为脚本或资源的属性添加,只有当该随机字符串与服务器生成的随机数匹配时才会执行。攻击者无法猜测随机字符串,因此无法使用有效的随机数调用脚本或资源,从而导致资源无法执行。
毛林
2025年9月6日 12:34
转发文档
收藏文档
上一篇
下一篇
手机扫码
复制链接
手机扫一扫转发分享
复制链接
Markdown文件
PDF文档(打印)
分享
链接
类型
密码
更新密码