计算机网络知识----网络安全----XSS(跨站脚本攻击 )

1,024 阅读10分钟

开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第16天,点击查看活动详情

一、简介

跨站脚本攻击,英文全称是Cross Site Script,本来缩写完成时CSS,但为了和层叠样式表(Cascading Style Sheet,CSS)区分开来,所以在网络安全中叫做XSS

XSS是一种代码注入攻击。攻击者通过在目标网站上注入恶意脚本,使之在用户的浏览器上运行。利用这些恶意脚本,攻击者可获取用户的敏感信息如 Cookie、SessionID 等,进而危害数据安全。

XSS的本质: 恶意代码未经过滤,与网站正常的代码混在一起;浏览器无法分辨哪些脚本是可信的,导致恶意脚本被执行。 而由于直接在用户的终端执行,恶意代码能够直接获取用户的信息,或者利用这些信息冒充用户向网站发起攻击者定义的请求。

1.2 如何攻击

我们在这简单的演示一个例子,来描述下是如何进行XSS攻击的

data: {
    content: '<img src="xxx" onerror="alert(123)">'
}
<div v-html="content">
</div>

1.3 危害性

  • 窃取Cookie
  • 按键记录或者钓鱼
  • 利用虚假输入表单骗取用户个人信息
  • 利用脚本窃取用户的 Cookie,被害者在不知情的情况下,帮助攻击者发送恶意请求
  • 显示伪造的文章或图片
  • 偷取网站任意数据
  • 偷取用户资料
  • 偷取用户密码和登录状态
  • 欺骗用户
  • 未授权操作
  • 获取页面数据
  • 获取 Cookies
  • 获取本地存储数据
  • 劫持前端逻辑
  • 发送请求
  • 图片、表单等
  • ...危害还有很多

二 XSS分类

  • 反射型 XSS:url
  • 存储型 XSS:数据持久化
  • DOM XSS:纯前端 DOM 操作

反射型和存储型在纯前端渲染中已经很少见了,尤其是在大量使用第三方框架的前提下。

例如:Vue,React,Angular,...

2.1 反射型 XSS

黑客往往诱导用户点击一个恶意链接,才能攻击成功。

攻击步骤:

  1. 攻击者构造出特殊的 URL,其中包含恶意代码。
  2. 用户打开带有恶意代码的 URL 时,网站服务端将恶意代码从 URL 中取出,拼接在 HTML 中返回给浏览器。
  3. 用户浏览器接收到响应后解析执行,混在其中的恶意代码也被执行。
  4. 恶意代码窃取用户数据并发送到攻击者的网站,或者冒充用户的行为,调用目标网站接口执行攻击者指定的操作。

场景: 通过 URL 传递参数的功能,如网站搜索、跳转等。由于需要用户主动打开恶意的 URL 才能生效,攻击者往往会结合多种手段诱导用户点击。

请看如下示例:

<h1 class="logo-font" v-html="$route.query.content"></h1>

请求方式:

http://localhost:8080/#/?content=<img src="xxx" onerror="alert(123)">

上面只是说明一些攻击方式,在自己实现过程中,需要搭建自己的服务端进行验证。例如,input输入框中输入一段脚本,后端将此数据返回给前端,这种情况,就形成了攻击,详细代码请戳

总结

如果开发者没有将用户输入的文本进行合适的过滤,就贸然插入到 HTML 中,这很容易造成注入漏洞。攻击者可以利用漏洞,构造出恶意的代码指令,进而利用恶意代码危害数据安全。

  • 在 HTML 中内嵌的文本中,恶意内容以 script 标签形成注入。
  • 在内联的 JavaScript 中,拼接的数据突破了原本的限制(字符串,变量,方法名等)。
  • 在标签属性中,恶意内容包含引号,从而突破属性值的限制,注入其他属性或者标签。
  • 在标签的 href、src 等属性中,包含 javascript: 等可执行代码。
  • 在 onload、onerror、onclick 等事件中,注入不受控制代码。
  • 在 style 属性和标签中,包含类似 background-image:url("javascript:..."); 的代码(新版本浏览器已经可以防范)。
  • 在 style 属性和标签中,包含类似 expression(...) 的 CSS 表达式代码(新版本浏览器已经可以防范)。

2.2 存储型XSS

存储型会把用户输入的数据“存储”在服务器端。

存储型 XSS 的攻击步骤:

  1. 攻击者将恶意代码提交到目标网站的数据库中。
  2. 用户打开目标网站时,网站服务端将恶意代码从数据库取出,拼接在 HTML 中返回给浏览器。
  3. 用户浏览器接收到响应后解析执行,混在其中的恶意代码也被执行。
  4. 恶意代码窃取用户数据并发送到攻击者的网站,或者冒充用户的行为,调用目标网站接口执行攻击者指定的操作。

请看如下场景:

  • 内容一般使用富文本编辑器,富文本编辑器最终产生的结果:HTML 格式的字符串
  • Web 后台就会把文章内容存储到数据库中,用于文章详情展示
  • 当有用户查看文章详情的时候,就会渲染文章内容
  • 若用户提交的数据中有script标签或者注入其他信息,未被处理的情况下保存到的数据库中。则查询时会出现XSS攻击

2.3 DOM XSS

通过修改页面的DOM节点形成的XSS,称之为DOM XSS,严格意义来说,效果和反射型XSS类似,之所以单独划分,是因为DOM XSS形成原因比较特别。

DOM 型 XSS 的攻击步骤:

  1. 攻击者构造出特殊的 URL,其中包含恶意代码。
  2. 用户打开带有恶意代码的 URL。
  3. 用户浏览器接收到响应后解析执行,前端 JavaScript 取出 URL 中的恶意代码并执行。
  4. 恶意代码窃取用户数据并发送到攻击者的网站,或者冒充用户的行为,调用目标网站接口执行攻击者指定的操作。

注意:DOM 型 XSS 攻击中属于前端 JavaScript 自身的安全漏洞,而其他两种 XSS 都属于服务端的安全漏洞。

三、XSS攻击防御

XSS防御是复杂的,流行的浏览器都内置了一些XSS防御措施,例如Firefox的CSP,Noscript扩展,IE8内置的XSS Filter等。

3.1 常用方法

3.1.1 使用HttpOnly

浏览器将禁止页面的Javascript访问带有HttpOnly属性的Cookie。

严格意义来说,HttpOnly并未为了对抗XSS,它解决的是XSS后的Cookie劫持攻击。

总结来说,HttpOnly有助于缓解XSS攻击,但仍然需要其他能够解决XSS漏洞的方案。

3.1.2 输入过滤

常见的漏洞如XSS,SQL Injection等,都需要攻击者构造一些特殊字符,这些特殊字符一般正常用户是不会用到的,所以输入过滤就显得尤为重要。

问题来了?输入过滤,需要在前端检查还是后端呢。

  1. 大部分人的想法是在前端检查,但是我们想一下,如果用户直接构造请求,那么就可以发送恶意代码了。

  2. 在后端存入数据库前进行过滤检查,然后将过滤后的信息返回给前端。

    1. a>b,转移过滤后是a&gt;b
    2. 后端存储数据库后,下次调用返回a&gt;b
    3. 若直接在html页面显示,则显示为a>b;
    4. 返回后,这个内容不能直接用于 Vue 等模板的展示,也不能直接用于内容长度计算,不能用于标题、alert 等。而且使用在android,ios,等系统上,是无法正常显示的。

总结: 输入侧过滤能够在某些情况下解决特定的 XSS 问题,但会引入很大的不确定性和乱码问题。在防范 XSS 攻击时应避免此类方法。

当然,对于明确的输入类型,例如数字、URL、电话号码、邮件地址等等内容,进行输入过滤还是必要的。

既然输入过滤并非完全可靠,我们就要通过“防止浏览器执行恶意代码”来防范 XSS。这部分分为两类:

  • 防止 HTML 中出现注入。
  • 防止 JavaScript 执行时,执行恶意代码。

结论:不建议在输入的时候对数据进行转义,而是在输出的时候。

3.1.3 转义 HTML

function escape(str) {
  str = str.replace(/&/g, '&amp;')
  str = str.replace(/</g, '&lt;')
  str = str.replace(/>/g, '&gt;')
  str = str.replace(/"/g, '&quto;')
  str = str.replace(/'/g, '&#39;')
  str = str.replace(/`/g, '&#96;')
  str = str.replace(///g, '&#x2F;')
  return str
}

一般是不会对空格进行转移的,而且日常开发过程中,可以使用jsxss库进行转义

3.1.4 配置白名单

对于富文来说,我们是没办法通过转义来防止攻击的。

配置白名单,比如,只允许<a><div>等比较安全的标签存在。

富文本中用户自定义的一些CSS,style也会导致XSS攻击。因此要尽可能的禁止用户自定义CSS与style。如果一定要自定义,则需要过滤CSS,对CSS进行智能解析,检测其中危险代码。

实际开发过程中,可以使用jsxss对富文本进行解析,也可以使用cheerio

3.2 预防存储型和反射型 XSS 攻击

前面讲到,存储型和反射型 XSS 都是在服务端取出恶意代码后,插入到响应 HTML 里的,攻击者刻意编写的“数据”被内嵌到“代码”中,被浏览器所执行。

预防这两种漏洞,我们常见的做法:

  • 改成纯前端渲染,把代码和数据分隔开。
  • 对 HTML 做充分转义。

转义这部分我们在之前已经详细的介绍过了,在这里我们就不做过度的赘述。

纯前端渲染

过程

  1. 浏览器先加载一个静态 HTML,此 HTML 中不包含任何跟业务相关的数据。
  2. 然后浏览器执行 HTML 中的 JavaScript。
  3. JavaScript 通过 Ajax 加载业务数据,调用 DOM API 更新到页面上。

在纯前端渲染中,我们会明确的告诉浏览器:下面要设置的内容是文本(.innerText),还是属性(.setAttribute),还是样式(.style)等等。浏览器不会被轻易的被欺骗,执行预期外的代码了。

3.3 预防 DOM XSS 攻击

DOM 型 XSS 攻击,实际上就是网站前端 JavaScript 代码本身不够严谨,把不可信的数据当作代码执行了。

例如:

<div id="t"></div>
<input type="text" id="text" value="" />
<input type="button" id="s" value="write" onclick="test()" />
<script>
    function test() {
        var str = document.getElementById("text").value;
        document.getElementById("t").innerHTML =
            "<a href='" + str + "'>testLink</a>";
    }
</script>

会触发DOM 型XSS的地方有很多:

  • document.write()
  • document.writeln()
  • xxx.innerHTML =
  • xxx.outerHTML=
  • innerHTML.replace
  • document.location.replace()
  • document.location.assign()
  • ...

除了上述直接操作,我们还需要重点关注

  • eval()
  • setTimeout()
  • setInterval()
  • 页面中的input框
  • DOM中的事件监听器:locationonclickonerroronloadonmouseover
  • a标签中的href属性

3.4 XSS 的检测

  1. 使用通用 XSS 攻击字串手动检测 XSS 漏洞

    jaVasCript:/*-/*`/*`/*'/*"/**/(/* */oNcliCk=alert() )//%0D%0A%0d%0a//</stYle/</titLe/</teXtarEa/</scRipt/--!>\x3csVg/<sVg/oNloAd=alert()//>\x3e
    

    能够检测到存在于 HTML 属性、HTML 文字内容、HTML 注释、跳转链接、内联 JavaScript 字符串、内联 CSS 样式表等多种上下文中的 XSS 漏洞,也能检测 eval()、setTimeout()、setInterval()、Function()、innerHTML、document.write() 等 DOM 型 XSS 漏洞,并且能绕过一些 XSS 过滤器。

  2. 使用扫描工具

    • Arachni:Arachni是基于Ruby的开源,功能全面,高性能的漏洞扫描框架
    • Mozilla HTTP Observatory:Mozilla最近发布的一款名为Observatory的网站安全分析工具
    • w3af:W3af是一个基于Python的Web应用安全扫描器

总结

XSS漏洞虽然复杂,但确可以彻底解决,在设计XSS解决方案的时候,应该深入理解XSS攻击原理,针对不同的场景使用不同的方法。