写在前面的话
现在已经是2017年了,想必大家一定知道什么是CSRF(跨站请求伪造)了,因为之前关于这个话题的讨论已经有很多了。这种漏洞已经存在了很多年,社区中也有非常详细的文档以及对应的解决方案,目前很多热门的网站开发框架基本上或多或少都实现了相应的缓解方案。
那我们在本系列文章中要讨论什么呢?请大家先思考以下几个因素:
- 遗留应用缺少CSRF保护;
- 某些框架的内置CSRF防护机制存在缺陷;
- 应用程序没有使用已证明安全的框架保护机制;
- 新的应用程序没有使用提供了CSRF保护功能的现代框架;
因此,对于目前的Web应用程序来说,CSRF仍然是一个相对普遍存在的安全漏洞。
在这篇文章中,我们首先会跟大家深入分析CSRF的工作机制,以及现代应用程序可以采用的安全措施。接下来,我们会给大家提供一份安全解决方案,同学们可以将其用于自己所开发的应用程序之中(不需要对源代码进行任何修改)。最后,我们会给大家测试一种针对cookie的新型扩展,如果它能够成为一种通用标准的话,它将能够消除绝大多数场景下的跨站脚本漏洞。除此之外,我们在GitHub库中提供了本系列文章中所要使用到的代码以及测试样例,有需要的同学可以自行下载。
了解攻击机制
简而言之,CSRF这种漏洞将允许攻击者强迫目标用户代表攻击者发送HTTP请求。这种攻击主要发生在客户端(例如Web浏览器),而在这种场景下目标用户所发送的应用程序信息是完全可信的,因此攻击者就可以成功实现攻击了。对于这种类型的攻击,我们需要关注以下三个因素:使用不安全的HTTP verb ,Web浏览器对cookie的处理、以及跨站脚本漏洞(XSS)。
HTTP标准将verb主要分成了安全的以及不安全的两大类。安全的verb(GET、HEAD以及OPTIONS)主要用于只读操作,使用了这些verb的请求用于返回与被请求资源有关的信息,并且不会对服务器端产生影响。不安全的verb(POST、PUT、PATCH和DELETE)主要用于对资源进行修改、创建和删除操作。但不幸的是,一个HTTP verb本身所要进行的操作是可以被忽略或者被篡改的。
导致HTTP verb使用不当的主要原因在于浏览器对HTTP标准的支持存在缺陷,这是一种历史遗留问题。在XML HTTP Request(XHR)流行起来之前,我们几乎得依靠特定框架和代码库来使用HTTP verb(除了GET和POST之外)。这种限制导致HTTP verb之间的区别界限变得十分模糊,虽然仅凭这一点并不能创建出CSRF的攻击场景,但这也让针对CSRF的保护变得更加难以实现了。对CSRF漏洞“帮助”最大的一个因素,就是浏览器处理cookie的方式了。
在设计之初,HTTP本身是一种无状态协议,即一个请求对应一个响应,请求之间不携带/交换任何的状态信息。为了支持复杂的Web应用程序,cookie就成为了一个维持相关HTTP请求之间状态的解决方案。浏览器的全局Cookie可以跨实例、窗口和标签进行共享,用户需要依赖于Web浏览器来自动化地给每一个请求发送cookie。由于cookie是可以在浏览器中进行访问或修改的,并且缺乏反篡改保护,因此请求状态的保存任务就转移到了服务器管理会话的身上。在这种模型下,服务器端需要生成一个唯一标识符并将其存储到cookie中。每一个浏览器在发送cookie时都需要发送这个唯一标识符,而服务器端就可以根据这种标识符来判断会话的有效性了。当会话终止之后,服务器端会丢弃这个标识符,之后使用该标识符的请求都将会被视为无效请求。
现在的主要问题是,浏览器如何去管理cookie。cookie主要由一系列属性组成,但其中最重要的是Domain属性。Domain属性的功能是将cookie限定到某个匹配domain属性的特定主机范围内,这是一种用于防止敏感信息(例如会话识别符)被恶意网站窃取(会话固定攻击)的安全机制。这里存在的漏洞是domain属性并不用遵循同源策略(SOP),它只会对cookie以及请求中服务器的domain值进行简单的对比。这也就意味着,任何不同源的请求只需要带有该主机的cookie,就可以向其发送请求了。在这种场景下,只有安全的以及不安全的HTTP verb能够得到正确使用,才能确保这种行为是安全的。关于同源策略的更多详细内容,请参考这篇文档。
最后一个需要关心的因素,就是跨站脚本(XSS)漏洞。XSS指的是攻击者控制JavaScript或HTML来给目标用户呈现DOM内容的能力。如果某个应用程序中存在XSS漏洞的话,那这个应用此时几乎就无法再抵御CSRF攻击了。如果XSS漏洞存在的话,本文所要介绍的以及目前绝大多数应用程序所依赖的应对措施就完全没有用了。
执行攻击
既然我们已经了解到了攻击成功所涉及到的相关因素,我们就可以继续深入了解CSRF的工作机制了。如果你还没有搭建好测试环境的话,请现在赶紧按照之前提供的GitHub库中的方法(参考README文档)来搭建环境并运行样本。
我们所要讨论的主要有以下三种CSRF:
- 资源包含(Resource inclusion)
- Form-based
- XMLHttpRequest
在绝大多数关于CSRF的演示样例或者基础课程之中,资源包含是这种类型是最常见的。这种类型的CSRF允许攻击者控制一个HTML标签中包含的资源,例如图片、视频、音频、对象、以及脚本等等。如果攻击者能够影响页面所加载的URL,则任何包含了远程资源的标签都将可以被攻击者所利用。正如之前所说的,由于缺乏同源检测,这种攻击并不需要XSS,而且任何能够控制目标网站的攻击者都能够实现攻击。这种类型的漏洞仅限适用于GET请求,因为这种请求是浏览器专门用来请求资源URL的,而且这种漏洞的限制就在于它要求不当使用安全的HTTP verb。
第二种类型是基于表单(form-based)的CSRF,一般出现在安全verb使用正确的情况下。在这种攻击场景中,攻击者要自行创建一个表单并欺骗用户提交该表单。表单中包含一段能够强迫浏览器提交该表单的JavaScript代码段,它不仅全部由隐藏元素组成,而且提交速度非常快,所以目标用户几乎无法察觉到。由于浏览器处理cookie的方式存在问题,因此这种表单可以托管在任何一个网站上,只要用户用有效的cookie完成了登录,攻击就能成功。第二种漏洞配合钓鱼攻击是比较好的。
我们所要讨论的最后一种类型即XMLHttpRequest(XHR),而这种情况是比较少见的,因为利用这种漏洞时所要满足的条件太多了。由于很多现代Web应用程序都依赖于XHR,因此我们需要花很多事件来构建并实现这种特定的应对措施。由于同源策略的存在,基于XHR的CSRF一般都是通过XSS Payload的形式来利用的。如果没有跨域资源共享(CORS),XHR将只能被限制于向特定源发送请求,这样也就限制了攻击者托管自己Payload的途径。这种漏洞的攻击Payload其实是一种标准XHR,攻击者可以想办法将其注入到目标用户浏览器的页面DOM中。
总结
在接下来的系列文章中,我们将会给大家介绍如何在真实的开发环境之中部署最有效的CSRF解决方案.