懂你网络系列9之网络安全中的XSS攻击

一.简介

通过系列8中同源的策略我们知道,由于其非绝对性,能通过在页面嵌入跨域资源,通过cors来来允许跨源访问和通过js中的API来实现跨文档消息机制,这些机制同时带了很多安全问题。

当页面被注入了恶意 JavaScript 脚本时,浏览器无法区分这些脚本是被恶意注入的还是正 常的页面内容,所以恶意注入 JavaScript 脚本也拥有所有的脚本权限。

这其中最典型的就是XSS(Cross-site scripting,XSS)攻击了,为了与“CSS”区分开来,故简称 XSS,翻译过来就 是“跨站脚本。是一种安全漏洞,攻击者可以利用这种漏洞在网站上注入恶意的客户端代码。

当被攻击者登陆网站时就会自动运行这些恶意代码,从而,攻击者可以突破网站的访问权限,冒充受害者。根据开放式 Web 应用安全项目(OWASP),XSS Z在2017 年被认为 7 种最常见的 Web 应用程序漏洞之一。

如果 Web 应用程序没有部署足够的安全验证,那么,这些攻击很容易成功。浏览器无法探测到这些恶意脚本是不可信的,所以,这些脚本可以任意读取 cookie,session tokens,或者其它敏感的网站信息,或者让恶意脚本重写HTML内容。

恶意内容一般包括 JavaScript,但是,有时候也会包括 HTML,FLASH 或是其他浏览器可执行的代码。XSS 攻击的形式千差万别,但他们通常都会:将 cookies 或其他隐私信息发送给攻击者,将受害者重定向到由攻击者控制的网页,或是经由恶意网站在受害者的机器上进行其他恶意操作。

XSS攻击分类

1.存储型XSS(持久型)

注入型脚本永久存储在目标服务器上。当浏览器请求数据时,脚本从服务器上传回并执行。
步骤
1.攻击者将恶意代码提交到目标网站的数据库中。

2.用户打开目标网站时,网站服务端将恶意代码从数据库取出,拼接在 HTML 中返回给浏览器。

3.用户浏览器接收到响应后解析执行,混在其中的恶意代码也被执行。

4.恶意代码窃取用户数据并发送到攻击者的网站,或者冒充用户的行为,调用目标网站接口执行攻击者指定的操作.

这种攻击常见于带有用户保存数据的网站功能,如论坛发帖、商品评论、用户私信等。

2.反射型 XSS(非持久性)

当用户点击一个恶意链接,或者提交一个表单,或者进入一个恶意网站时,注入脚本进入被攻击者的网站。Web服务器将注入脚本,比如一个错误信息,搜索结果等 返回到用户的浏览器上(反射)。由于浏览器认为这个响应来自"可信任"的服务器,所以会执行这段脚本。
步骤
1.攻击者构造出特殊的 URL,其中包含恶意代码。

2.用户打开带有恶意代码的 URL 时,网站服务端将恶意代码从 URL 中取出,拼接在 HTML 中返回给浏览器。

3.用户浏览器接收到响应后解析执行,混在其中的恶意代码也被执行。

4.恶意代码窃取用户数据并发送到攻击者的网站,或者冒充用户的行为,调用目标网站接口执行攻击者指定的操作。

值得注意的是Web 服务器不会存储反射型 XSS 攻击的恶意脚本,这是和存储型 XSS 攻击不同的地方。因此它是非持久性的

反射型 XSS 漏洞通过 URL 传递参数的功能,如网站搜索、跳转等。

由于需要用户主动打开恶意的 URL 才能生效,攻击者往往会结合多种手段诱导用户点击。

POST 的内容也可以触发反射型 XSS,只不过其触发条件比较苛刻(需要构造表单提交页面,并引导用户点击),所以非常少见。

3.基于 DOM 的 XSS

通过修改原始的客户端代码,受害者浏览器的 DOM 环境改变,导致有效载荷的执行。也就是说,页面本身并没有变化,但由于DOM环境被恶意修改,有客户端代码被包含进了页面,并且意外执行。

步骤:
1.攻击者构造出特殊的 URL,其中包含恶意代码。
用户打开带有恶意代码的 URL。

2.用户浏览器接收到响应后解析执行,前端 JavaScript 取出 URL 中的恶意代码并执行。

3.恶意代码窃取用户数据并发送到攻击者的网站,或者冒充用户的行为,调用目标网站接口执行攻击者指定的操作。

三.XSS攻击防范

由前面的三种攻击方式可知存储型 XSS 攻击和反射型 XSS 攻击都是需要经过 Web 服务器来处理的,因此可 以认为这两种类型的漏洞是服务端的安全漏洞。而基于 DOM 的 XSS 攻击全部都是在浏览器端完成的,因此基于 DOM 的 XSS 攻击是属于前端的安全漏洞。

通过前面的描述可以知道一个XSS的攻击可以抽象为以下三个步骤,根据这三点我们可以定制更加立体的防御方式:
1.输入:攻击者提交恶意代码
2.发送:恶意消息的发送到服务器。
3.执行:浏览器执行恶意代码

1.在前端和服务端对输入的脚本进行过滤或者转码

用户在通过一些前端控件提交数据时,无论是提交到本地还是服务端,想由前端的过滤进行后再进行提交,过滤掉一些非法的字符,如用户通过输入恶意的代码,例如:

html:<script>alert("你被xss攻击了")</script>

过滤后整个script标签内容都没了,那么这段脚本是不可能执行了。
再者还可以通过转码:如上述在html中的代码被转成:

&lt;script&gt;alert(&#39;你被XSS攻击了&#39;)&lt;/script&gt

经过转码之后的内容,如script标签被转换为<script>,因此即使这段脚本 返回给页面,页面也不会执行这段脚本。

但是道高一尺,魔高一丈,上述这些在前端过滤和转码的普通的手段是不够了,有一些代码会绕过 过滤和转码:如:

<scr <script> ipt> alealertrt(你被xss攻击了)</ scr </ script> ipt>

绕过后执行:

<script> alert ("你被xss攻击了") </ script> 

这就需要采用业内比较成熟的,通用的过滤转码库了,如果使用的是vue ,node,react等框架,可以安装其提供的相应的插件。

有的甚至直接绕过了前端部分的防御,直接发送到服务器上,这就需要在发送和执行的层面对其进行防御了,服务端也有比较成熟的解决方案了

2.利用CSP

服务器端执行过滤或者转码可以阻止 XSS 攻击的发生,但完全依靠服务器端依然是不够的,这种单一的开发手段任然是不不够的。

Content Security Policy (CSP),内容安全策略是一个额外的安全层,用于检测并削弱某些特定类型的攻击,包括跨站脚本 (XSS) 和数据注入攻击等。无论是数据盗取、网站内容污染还是散发恶意软件,这些攻击都是主要的手段。

实施严格的 CSP 可以有效地防范 XSS 攻击,可以起到以下作用:

  • CSP通过指定有效域——即浏览器认可的可执行脚本的有效来源——使服务器管理者有能力减少或消除XSS攻击所依赖的载体。

  • 一个CSP兼容的浏览器将会仅执行从白名单域获取到的脚本文件,忽略所有的其他脚本 (包括内联脚本和HTML的事件处理属性)。

  • 禁止向第三方域提交数据,这样用户数据也不会外泄。

  • 还提供了上报机制,这样可以帮助我们尽快发现有哪些 XSS 攻击,以便尽快修复问题。

使用
为使CSP可用, 你需要配置你的网络服务器返回 Content-Security-Policy HTTP头部。
例如:
一个网站管理者允许内容来自信任的域名及其子域名 (域名不必须与CSP设置所在的域名相同)

ontent-Security-Policy: default-src 'self' *.trusted.com

除此之外, 元素也可以被用来配置该策略, 例如:

<meta http-equiv="Content-Security-Policy" content="default-src 'self'; img-src https://*; child-src 'none';">

更多具体使用方法见:Content-Security-Policy

3.利用httpOnly属性

为避免跨域脚本 (XSS) 攻击,通过JavaScript的 Document.cookie API无法访问带有 HttpOnly 标记的Cookie,它们只应该发送给服务端。如果包含服务端 Session 信息的 Cookie 不想被客户端 JavaScript 脚本调用,那么就应该为其设置 HttpOnly 标记 例如:

Set-Cookie: id=a3fWa; Expires=Wed, 21 Oct 2015 07:28:00 GMT; Secure; HttpOnly

标记为 Secure 的Cookie只应通过被HTTPS协议加密过的请求发送给服务端。但即便设置了 Secure 标记,敏感信息也不应该通过Cookie传输,因为Cookie有其固有的不安全性,Secure 标记也无法提供确实的安全保障。

4.合理编码防御DOM型XSS攻击

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

在使用 .innerHTML、.outerHTML、document.write() 时要特别小心,不要把不可信的数据作为 HTML 插到页面上,而应尽量使用 .textContent、.setAttribute() 等。

如果用 Vue/React 技术栈,并且不使用 v-html/dangerouslySetInnerHTML 功能,就在前端 render 阶段避免 innerHTML、outerHTML 的 XSS 隐患。

DOM 中的内联事件监听器,如 location、onclick、onerror、onload、onmouseover 等, 标签的 href 属性,JavaScript 的 eval()、setTimeout()、setInterval() 等,都能把字符串作为代码运行。如果不可信的数据拼接到字符串中传递给这些 API,很容易产生安全隐患,请务必避免。

<!-- 内联事件监听器中包含恶意代码 -->
![](https://awps-assets.meituan.net/mit-x/blog-images-bundle-2018b/3e724ce0.data:image/png,)

<!-- 链接内包含恶意代码 -->
<a href="UNTRUSTED">1</a>

<script>
// setTimeout()/setInterval() 中调用恶意代码
setTimeout("UNTRUSTED")
setInterval("UNTRUSTED")

// location 调用恶意代码
location.href = 'UNTRUSTED'

// eval() 中调用恶意代码
eval("UNTRUSTED")
</script>

如果项目中有用到这些的话,一定要避免在字符串中拼接不可信数据。

5.其他防御措施

  • 输入内容长度控制:受信任的输入,都应该限定一个合理的长度。虽然无法完全防止 XSS 发生,但可以增加 XSS 攻击的难度
  • 验证码:防止脚本冒充用户提交危险操作。
  • 等等。。。

参考资料

Cross-site scripting | MDN

Cross-site scripting on Wikipedia

Cross-site scripting on OWASP

XSS Attack – Exploit & Protection

前端安全系列(一):如何防止XSS攻击?

posted @ 2020-05-28 21:22  圣雁易行  阅读(335)  评论(0编辑  收藏  举报