XSS攻击通常指的是通过利用网页开发时留下的漏洞,通过巧妙的方法注入恶意指令代码到网页,使用户加载并执行攻击者恶意制造的网页程序。这些恶意网页程序通常是JavaScript,但实际上也可以包括Java、 VBScript、ActiveX、 Flash 或者甚至是普通的HTML。攻击成功后,攻击者可能得到包括但不限于更高的权限(如执行一些操作)、私密网页内容、会话和cookie等各种内容。
XSS简介
XSS全称是Cross Site Scripting(为了和CSS进行区分,就叫XSS)即跨站脚本,是指恶意攻击者往Web页面里插入恶意Script代码,当用户浏览该页之时,嵌入其中Web里面的Script代码会被执行,从而达到恶意攻击用户的目的。
XSS分类
反射型 XSS
反射型 XSS 的攻击步骤:
- 攻击者构造出特殊的 URL,其中包含恶意代码。
- 用户打开带有恶意代码的 URL 时,网站服务端将恶意代码从 URL 中取出,拼接在 HTML 中返回给浏览器。
- 用户浏览器接收到响应后解析执行,混在其中的恶意代码也被执行。
- 恶意代码窃取用户数据并发送到攻击者的网站,或者冒充用户的行为,调用目标网站接口执行攻击者指定的操作。
反射型 XSS 漏洞常见于通过 URL 传递参数的功能,如网站搜索、跳转等。
由于需要用户主动打开恶意的 URL 才能生效,攻击者往往会结合多种手段诱导用户点击。
POST 的内容也可以触发反射型 XSS,只不过其触发条件比较苛刻(需要构造表单提交页面,并引导用户点击),所以非常少见。
存储型XSS
存储型 XSS 的攻击步骤:
- 攻击者将恶意代码提交到目标网站的数据库中。
- 用户打开目标网站时,网站服务端将恶意代码从数据库取出,拼接在 HTML 中返回给浏览器。
- 用户浏览器接收到响应后解析执行,混在其中的恶意代码也被执行。
- 恶意代码窃取用户数据并发送到攻击者的网站,或者冒充用户的行为,调用目标网站接口执行攻击者指定的操作。
这种攻击常见于带有用户保存数据的网站功能,如论坛发帖、商品评论、用户私信等。
反射型 XSS 跟存储型 XSS 的区别是:反射型 XSS 的恶意代码存在 URL 里,存储型 XSS 的恶意代码存在数据库里。
DOM 型 XSS
DOM 型 XSS 的攻击步骤:
- 攻击者构造出特殊的 URL,其中包含恶意代码。
- 用户打开带有恶意代码的 URL。
- 用户浏览器接收到响应后解析执行,前端 JavaScript 取出 URL 中的恶意代码并执行。
- 恶意代码窃取用户数据并发送到攻击者的网站,或者冒充用户的行为,调用目标网站接口执行攻击者指定的操作。
DOM 型 XSS 跟前两种 XSS 的区别:DOM 型 XSS 攻击中,取出和执行恶意代码由浏览器端完成,属于前端 JavaScript 自身的安全漏洞,而其他两种 XSS 都属于服务端的安全漏洞。
攻击方式
常用的XSS攻击手段和目的有:
盗用cookie,获取敏感信息。
利用植入Flash,通过crossdomain权限设置进一步获取更高权限;或者利用Java等得到类似的操作。
利用iframe、frame、XMLHttpRequest或上述Flash等方式,以(被攻击)用户的身份执行一些管理动作,或执行一些一般的如发微博、加好友、发私信等操作。
利用可被攻击的域受到其他域信任的特点,以受信任来源的身份请求一些平时不允许的操作,如进行不当的投票活动。
在访问量极大的一些页面上的XSS可以攻击一些小型网站,实现DDoS攻击的效果
XSS防御
防御XSS的七条原则
- 不要在页面中插入任何不可信数据,除非这些数已经据根据下面几个原则进行了编码
- 在将不可信数据插入到HTML标签之间时,对这些数据进行HTML Entity编码
- 在将不可信数据插入到HTML属性里时,对这些数据进行HTML属性编码
- 在将不可信数据插入到SCRIPT里时,对这些数据进行SCRIPT编码
- 在将不可信数据插入到Style属性里时,对这些数据进行CSS编码
- 在将不可信数据插入到HTML URL里时,对这些数据进行URL编码
- 使用富文本时,使用XSS规则引擎进行编码过滤
HttpOnly
HttpOnly
最早是由微软提出,并在 IE 6
中实现的,至今已经逐渐成为一个标准,各大浏览器都支持此标准。具体含义就是,如果某个 Cookie
带有 HttpOnly
属性,那么这一条 Cookie
将被禁止读取,也就是说,JavaScript
读取不到此条 Cookie
,不过在与服务端交互的时候,Http Request
包中仍然会带上这个 Cookie
信息,即我们的正常交互不受影响。
输入检查
记住一点:不要相信任何输入的内容。
无论是不是做了安全校验,都必须进行过滤操作,而且需要后台配合过滤,如果后端的检查校验还做得不好,那就可能被攻破。
输入检查在更多的时候被用于格式检验,例如用户名只能以字母和数字组合,手机号码只能有 11 位且全部为数字,否则即为非法。
这些格式检查类似于白名单效果,限制输入允许的字符,让一下特殊字符的攻击失效。
目前网上有很多开源的 XSS Filter
,这些 XSS Filter
目前来说还是有些效果的,能只能检验输入内容,高级一点的还会匹配 XSS
特征,例如内容是否包含了 <script>
,javascript
等敏感字符,但是这些 XSS Filter
只是获取到了用户的输入内容,并不了解其上下文含义,很多时候会误过滤。
输出检查
不要以为在输入的时候进行过滤就万事大吉了,恶意攻击者们可能会层层绕过防御机制进行 XSS
攻击,一般来说,所有需要输出到 HTML
页面的变量,全部需要使用编码或者转义来防御。
HTMLEncode
针对 HTML
代码的编码方式是 HTMLEncode
,它的作用是将字符串转换成 HTMLEntities
。
目前来说,为了对抗 XSS
,以下转义内容是必不可少的:
特殊字符 | 实体编码 |
---|---|
& | & |
< | < |
> | > |
“ | " |
‘ | ' |
/ | / |
JavaScriptEncode
JavaScriptEncode
与 HTMLEncode
的编码方式不同,它需要用 \
对特殊字符进行转义。
在对抗 XSS
时,还要求输出的变量必须在引号内部,以免造成安全问题,可是很多开发者并没有这种习惯,这样只能使用更为严格的 JavaScriptEncode
来保证数据安全:除了数字,字符之外的所有字符,小于127的字符编码都使用十六进制 \xHH
的方式进行编码,大于用unicode(非常严格模式)。
正确的防御 XSS
XSS
的本质还是一种 HTML 注入
,用户的数据被当成了 HTML
代码一部分来执行,从而混淆了原本的语意,产生了新的语意。
如果网站使用了 MVC(MVVM)
结构,那么 XSS
就会发生在 View
层,也就是变量拼接到页面时产生的,所以在用户提交数据的时候进行输入检查,并不是真正在被攻击的地方做防御.
想要根治XSS问题,可以列出所有XSS可能发生的场景,再一一解决。
在 HTML
标签中输出
在 HTML
标签中直接输出变量,没有做任何处理,会导致 XSS
。
1 | <a href=# ><img src=# onerror=alert(1)/></a> |
这种方式的解决方案是,所有需要输出到页面的元素全部通过 HTMLEncode
。
在 HTML
属性中输出
在和 HTML
标签中输出攻击方式类似,只不过输出的内容会自动闭合标签。
1 | <div>$var</div> |
这种方式的防御方法仍然是 HTMLEncode
。
在 <script>
标签中输出
假设我们的变量都在引号内部:
1 | <script> |
攻击者只需要闭合标签就能实行攻击,目前的防御方法为 JavaScriptEncode
。
在 CSS
中输出
在 CSS
中或者 style
标签或者 style attribute
中形成的攻击花样非常多,总体上类似于下面几个例子:
1 | <style>@import url('http:xxxxx')</style> |
要解决 CSS
的攻击问题,一方面要严格控制用户将变量输入style
标签内,另一方面不要引用未知的 CSS
文件,如果一定有用户改变 CSS
变量这种需求的话,可以使用 OWASP ESAPI
中的 encodeForCSS()
函数。
1 | String safe = ESAPI.encoder().encodeForCSS(request.getParameter("input")); |
在 URL
中输出
在地址张输出也比较复杂。一般来说 URL
的 path
或者 search
中进行攻击直接使用 URLEncode
即可。URLEncode
会将字符串转换为 %HH
的形式,类似空格就是 %20
。
可能的攻击方法就是:
1 | <!-- 原始 URL --> |
如果整个 URL
被用户控制,这时url的Protocal和Host是不能被URLEncode转义的。
一个 URL
的组成如下:
1 | [Protocal][Host][Path][Search][Hash] |
例如:
1 | https://www.evil.com/a/b/c/test?abc=123#ssss |
一般来说,如果变量是整个 URL
,则应该先检查变量是否以 http
开头,在此之后再对里面的变量进行 URLEncode
。
富文本处理
在一些网站,网站允许用户富含 HTML
标签的代码,比如文本里面要有图片、视频之类,这些文本展现出来全都是依靠 HTML
代码来实现。
那么,我们需要如何区分安全的 富文本
和 XSS
攻击呢?
基本的思想就是:
- 首先进行输入检查,保证用户输入的是完整的
HTML
代码,而不是有拼接的代码 - 通过
htmlParser
解析出HTML
代码的标签、属性、事件 富文本
的事件
肯定要被禁止,因为富文本
并不需要事件
这种东西,另外一些危险的标签也需要禁止,例如:<iframe>
,<script>
,<base>
,<form>
等- 利用白名单机制,只允许安全的标签嵌入,例如:
<a>
,<img>
,div
等,白名单不仅仅适用于标签,也适用于属性
- 过滤用户
CSS
,检查是否有危险代码
《白帽子讲web安全》