0x00 概述
概述了XSS漏洞的主要类型和利用方式、防御手段
0x01 漏洞简介
Cross-Site Scripting(为了与CSS区别,安全领域叫做XSS)攻击是注入的一种,主要是将恶意脚本注入到可信站点的HTML页面等网页文件中,当攻击者将注入恶意脚本的网页连接发送给用户,用户打开链接,浏览器会认为恶意脚本来自可信站点而执行
XSS攻击发生在
- 数据通过不可信源进入Web应用程序,最常见的是Web请求
- 数据被包含在动态内容中,该动态内容被发送到Web用户而不被验证为恶意内容,发送到Web浏览器的恶意内容通常采用JS的一种形式,但也可以包括HTML、Flash或者浏览器可执行的任何其他类型代码
XSS的攻击效果
- 基于XSS的攻击种类几乎是无限的
- 它们通常包括向攻击者发送诸如Cookie或其他会话信息之类的私有数据,将受害者重定向到攻击者控制的Web内容,或者在用户的机器上执行其他恶意操作
- 因为客户端JS等脚本语言的灵活性,XSS攻击可以和其他攻击方式在合适场景中相结合,造成更大的破坏
举个例子
服务端处理逻辑
@app.route("/")
def index():
param = request.args.get("param", "")
response = make_response("<p>Hello {}<p>".format(param))
response.set_cookie("key", "value", httponly=False) # 关闭框架自带的httponly=true
response.headers["X-XSS-Protection"] = 0 # Chrome本身的XSS防御关闭
return response
构造一个恶意URLhttp://test.com/param=<script>alert(document.cookie)</script>
浏览器打开后可以看到页面的反馈,弹出一个弹框,带有设置的key: value
的Cookie
实际攻击可以将上述代码逻辑修改为,通过Ajax把Cookie信息发送到攻击者服务器,攻击者就可以盗取用户身份访问站点
0x02 威胁场景
Reflected XSS(反射型XSS)
反射型XSS是用户输入直接作为响应的一部分作为服务器输出,如用户提交的表单、URL作为错误信息、搜索结果返回给用户,攻击者构造恶意的URL,Web表单发送给用户,用户点击就会触发
比如上面漏洞介绍里举的例子就是反射型XSS
Stored XSS(存储型XSS)
存储型XSS会把用户输入的数据“存储”到服务端,往往是数据库,往往具有更强的稳定性和破坏性,产生这种漏洞的页面功能往往是 往往是论坛消息、访问者日志、评论字段等
举个例子
我们在类似QQ空间的Web系统中提交文章和评论,会被存储在服务器数据库中,我们可以根据文章ID查询到文章内容
文章的显示页面逻辑往往如下
@app.route("/")
def index():
post_id = request.args.get("id", "")
post = db.query(post_id=post_id).first()
response = make_response("<textview> {}<textview>".format(post.content))
response.set_cookie("key", "value", httponly=False) # 关闭框架自带的httponly=true
response.headers["X-XSS-Protection"] = 0 # Chrome本身的XSS防御关闭
return response
可以看到逻辑和上面反射型XSS基本是一样的,当用户将<script>alert(document.cookie)</scirpt>
一类的攻击脚本写入文章,其他用户访问时便会触发XSS
存储型XSS由于不用在URL中构造恶意脚本也更容易对用户进行钓鱼,不易被识别
DOM Based XSS(DOM型的XSS)
以上两种XSS是根据恶意脚本是否能持久化(保存在服务端)进行划分的,DOM Based XSS从效果上来说也是反射型XSS,但因为成因特别,就单独作为一类
我们把修改页面DOM节点形成的XSS,称为DOM Based XSS
比如前端页面的代码为
<p>Select your language:</p>
<select>
<script>
document.write("<OPTION value=1>"+document.location.href.substring(document.location.href.indexOf("default=")+8)+"</OPTION>");
document.write("<OPTION value=2>English</OPTION>");
</script>
</select>
普通情况下的正常请求是http://www.some.site/page.html?default=French
恶意请求http://www.some.site/page.html?default=<script>alert(document.cookie)</script>
我们发现上面这样的请求和反射型XSS差不多,那么为什么DOM型XSS要单独分为一类呢
还是上面的页面,假如我们的服务器对XSS有着非常好的过滤和编码方式,请求从服务器返回时类似<script>,document.cookie,alert
这些字段都去掉了,那有什么办法再生成XSS攻击呢
我们把URL变成http://www.some.site/page.html#default=<script>alert(document.cookie)</script>
,我们可以发现此时攻击仍然能够生效
URL有个特点,#
被称为锚点,用于定位页面位置,#
符号后的内容不会传输到服务器
再进一步思考,反射型和存储型XSS中,参数值为恶意脚本,服务器端没有经过检查和过滤将恶意脚本拼接到了HTML页面中,浏览器加载页面执行脚本造成了XSS攻击,而通过URL锚点触发的XSS没有经过服务器,浏览器充当服务器角色拼接脚本到HTML页面中
笔者认为在B/S构架中,越来越灵活的Web使得浏览器和JS充当了一个客户端里的小型服务器,对用户的部分响应不需要再经过真正服务端的处理,预置在页面JS脚本中的一部分函数和功能直接处理了用户输入输出,或者对服务器的返回进行再处理,因此DOM型XSS也是针对浏览器和JS这个小型服务器的攻击,才被单独作为一类
除了上面例子中href.location
获取参数外,还要注意一些请求参数在服务器端经过一定的编码和过滤,但仍然可以通过DOM的改变对这些参数再一次进行处理,所以要注意防御XSS是防御最后浏览器显示的页面,而对DOM改变的操作相当于返回给用户的页面不用从真正服务端给出,而是浏览器和JS给出的
XSS的利用方式
基本上XSS能够做到事就是JS一类的脚本能做到的事,这里举一些XSS的利用方式,但不具体说明
- 盗取用户Cookie(登录态)
- 构造GET、POST等HTTP方法模拟网站逻辑以劫持用户身份执行(类似CSRF)
- 盗取页面数据,如一下用户敏感信息
- 识别用户浏览器
- 识别用户访问历史
- XSS蠕虫
0x03 修复方式
编码转换
这是防御XSS中最重要最有效的一步,服务端对要输出给用户的Web文件内容做以下编码转换(实体编码),同理,如果JS改变了DOM,也要在JS中对数据内容进行以下编码,对于用户的输入输出更要重点处理
& --> &
< --> <
> --> >
" --> "
/ --> /
' --> ' 单引号也有转换成'的情况,但是不推荐,因为这种转换不在HTML的规范中,而是在XML和XHTML的规范中
随着前端技术的发展,使用类似Angular JS
、VUE JS
的框架,本身自带编码转换,了解相关模块的配置之后使用
使用Cookie的httponly标识
为Cookie字段带上httponly的标识,可以防止JS读取Cookie信息,因此可以有效防止XSS攻击中盗取用户Cookie的能力
但是这种方式不是防御了XSS漏洞,只是减少了XSS漏洞造成的损失,但依旧十分有效,前端页面不需要读取Cookie的话尽量为每个Cookie配置这个标识
使用X-XSS-Protection头部进行防御
浏览器本身内置了XSS的防御审计工具,可以通过X-XSS-Protection进行配置,但是浏览器的防御攻击也是人写的代码,本身具有安全隐患
建议在看明白上文后使用,建议按需求使用X-XSS-Protection: 1; mode=block
和X-XSS-Protection: 0
两种配置
XSS过滤
存在一些情况,比如富文本,不能使用HTML实体编码的方式进行XSS防御,因为富文本中需要用到HTML标签来显示,在了解需求的情况下使用白名单进行过滤,因为XSS攻击的灵活只推荐使用白名单
设置白名单需要对XSS的可以利用的HTML标签和位置有一定了解,参考OWASP的开源项目anti-samy
不使用Flash
Flash已淘汰,不推荐使用
0x04 深入攻防
无需讲解,靠手熟,熟了之后靠漏洞情报,推荐一些漏洞靶场(关卡)
靶场一:http://xss-quiz.int21h.jp/
答案自己探索
0x05 总结
本文简介的XSS漏洞的类型、成因、修复方式等等
XSS的漏洞难点在系统越复杂、交互越多越容易发生问题,并且因为其灵活,容易绕过过滤手段
对于开发来说,对XSS有了解,有防御的意识,在非必要使用一些标签的情况下,对输出进行实体编码,一定要使用标签时,有考虑防御XSS的问题即可
0x06 参考资料
[OWASP XSS_(Cross_Site_Scripting)Prevention_Cheat_Sheet](https://www.owasp.org/index.php/XSS(Cross_Site_Scripting)_Prevention_Cheat_Sheet)