跨站请求伪造(Cross-site request forgery,常缩写为XSRF或CSRF)是利用Web应用和已验证用户之间的现有信任关系,强制这些用户为攻击者提交任意敏感事务的应用攻击。在安全文献中,这种攻击通常被归类为混淆代理人(Confused deputy)攻击的一种表现。这种情况下的代理人是Web应用客户浏览器,混淆是指浏览器无法正确地区分合法和未授权的请求。
尽管XSRF攻击有极其危险的特性,但是这些攻击和更容易理解的Web应用漏洞如XSS相比较少受到人们的注意。到了2006年,XSRF还被称为“沉睡的巨人”,在2007年之前还未被列入到OWASP Top 10项目中。甚至在本书编写的时候,流行的应用网站仍然不断地报告XSRF漏洞。
读者们可能感到奇怪,如果XSRF漏洞有这么大的风险,为什么直到现在还没有引起人们的注意?对这个问题有各种意见,但是部分原因无疑是因为这种漏洞是HTTP规范的无状态特性所固有的,这种特性要求验证令牌(通常是会话ID cookie和附加的验证令牌的组合)与每次请求一起发送。常识上,安全漏洞通常是由于应用开发人员在设计和开发中或者管理员在部署中所犯的错误导致的。与此相反,XSRF漏洞在开发人员在应用中忽略了XSRF预防机制时发生。换句话说,如果应用人员没有主动地防御这一问题而他们的应用支持敏感验证事务,那么应用默认情况下一般就容易遭到攻击,很少有例外。
那么XSRF攻击由什么组成呢?经典的示例是使用简单的HTTP GET请求,允许用户从一个账户向另一个账户转移资金的银行应用。假定账户转移操作采用如下形式:
http://xsrf.vulnerablebank.com/transferFunds.aspx?toaccount=12345&funds=1000.00¤cy=dollars
继续上面的例子,假定攻击者在其控制之下的系统上创建一个恶意的HTML页面,包含如下JavaScript代码:
<script type="text/javascript"> var i = document.createElement("image"); i.src = "http://xsrf.vulnerablebank.com/transferFunds.aspx? toaccount=EVIL_ATTACKER_ACCNT_NUMBER&funds=1000.00¤cy=dollars"; </script>
这段JavaScript代码的效果是创建一个动态的HTML图像标记(<img...>)将来源设置为这个脆弱的银行应用上的资金转移操作。受骗访问这个恶意网页的已验证银行网站用户的客户端浏览器将会运行攻击者的JavaScript,为动态图像的来源(这个例子中是资金转移操作)创建一个后台HTTP GET请求,这个操作将被执行,就像用户愿意这么做一样。这里要记住的关键一点是,任何时候当浏览器向其他域的资源发出请求,与该域相关的cookie、端口和路径将自动连接到HTTP首部并且与请求一起发送。这当然也包含了用于标识已验证用户的会话cookie。结果是攻击者成功地迫使银行用户将资金从其账户转移到攻击者的账户。
虽然这个示例有些勉强,只是用来阐述基本的问题,但是据报道,相似的漏洞已经在真正的系统上出现,可能导致这些容易遭受攻击的组织出现严重的财务损失。例如,2006年,安全邮件列表Full Disclosure上报道,Netflix很容易遭到跨站请求伪造问题的损害,根据最先揭露这一漏洞的David Ferguson所说,可能导致如下问题:
·在他的租借队列中增加电影
·在他的租借队列最前面添加电影
·修改账户的姓名和地址
·启用/禁用额外的电影信息
·修改账户的邮件地址和密码
·取消账户(未经证实/猜测)
幸运的是,Netflix漏洞在遭受真正的损失之前得以揭露。但是,从这种漏洞可能导致的操作列表中可以看到,对Netflix用户基础的成功攻击可能造成的潜在实际财务损失和对Netflix品牌的破坏不能低估。
应该注意的是,虽然用于阐述这个问题的示例是一个HTTP GET请求,但是HTTP POST请求也容易遭到攻击。一些开发人员似乎有了误解,认为将容易遭受攻击的GET请求改为POST请求就足以弥补XSRF漏洞。但是,POST请求并不会给攻击者带来多大的困难,他们只是必须构建JavaScript,使表单的构建和POST自动化。为了阻止浏览器在POST提交时自动地将受害用户重定向到易受攻击的应用,可以在恶意页面的隐含标记iframe中嵌入JavaScript。按照一般的应用设计规则,任何后续的操作应该使用HTTP POST请求构建。
跨站请求伪造攻击对策
避免XSRF攻击主要有三种常见的方法:
·两次传递的cookie: 在两次传递cookie的缓和技术中,用于提交敏感事务的每个表单生成时具有一个包含当前用户会话ID值或者其他安全生成的存储在客户端cookie中的随机值的隐藏输入域。当表单传递时,应用服务器将检查表单中的cookie值是否与HTTP请求首部中接收到的值相同。如果这两个值不同,请求将被作为无效请求拒绝,并将生成审计日志记录可能的攻击。这种方法依赖于攻击者不知道客户端会话cookie值。如果该值通过其他渠道泄露,这种策略将不会成功(会话劫持攻击仍然成为需要考虑的因素)。
·唯一的表单临时值: 唯一表单临时值补救策略可能是避免XSRF攻击的最常见方法。在这种方法中,每个请求构建的每个表单具有一个隐藏输入域,包含安全生成的随机临时值(Nonce)。这个临时值必须使用加密的安全伪随机码生成器生成,否则就容易遭到攻击。当应用服务器接受作为HTTP POST请求的一部分的表单参数值时,它将比较临时值和内存中存储的值,如果这两个值不同或者临时值超时则将请求作为无效请求拒绝。如果应用需要为每个包含敏感事务表单的请求生成和关联临时值以及临时值超时值,这种方法可能很难实现。有些开发框架实现现成的提供相似功能的例程,例如,Microsoft的ASP.NET ViewState功能在回传之间保持表单状态的变化。
·要求验证凭据: 这种补救方法需要已验证的用户在进行敏感事务时重新输入对应已验证会话的密码。这种策略常见于具有少数敏感事务的Web应用。以这种风格保证安全的常见应用领域是用户简档数据更新表单。应该小心地在这些页面上包含审计和锁定功能,以阻止重复地用随机猜测的密码尝试更新简档数据的XSRF验证暴力攻击。
为了说明银行资金转移操作是如何使用唯一表单临时值解决方案进行补救的,考虑下面的表单操作:
<form id="fundsTransfer" method="POST" action="transferFunds.aspx"> <input type="textbox" name="funds" value="0.00"> <input type="textbox" name="toaccount" value"=""> <!-- other input fields as needed --> <input type="hidden" name="xsrfToken" value="eozMKoWO6g3cIUa13y5wLw=="> </form>
注意,表单里已经添加了一个附加的隐含参数xsrfToken。新的xsrfToken值在每次为对应页面发出请求时使用加密的安全伪随机码生成器随机生成。因为攻击者不知道这个值,他将无法创建恶意的表单来伪造资金转移事务。
开发人员也应该在确定处理这个问题的方法时熟悉平台专用的内建XSRF预防技术,因为这种解决方案能够大大地减少加强应用安全所需要的工作量。但是一般来说,平台专用的技术将使用前面提到的某种策略(最可能是唯一表单临时值)。关于XSRF缓解技术更详细的信息可以在本章结尾的“参考与延伸阅读”中找到。
识别跨站请求伪造漏洞
有了前一个小节中列出的补救策略,识别Web应用中的XSRF漏洞是简单的活动。如果所关心的应用表单包含了唯一的临时值、难以猜测的cookie值或者需要验证凭据的参数,那么表单就不容易遭到XSRF的攻击。但是,如果表单不包含攻击者不易猜测的值,攻击者就能在第三方网站上重建表单,进行XSRF攻击。