随着智能手机的兴起,越来越多的人开始接入互联网,同时,以此为基础的灰产也在蒸蒸日上。虽然几乎全社会都浸淫在移动互联网中,但技术对他们来说是透明的。对于可用性上来说,这是极好的,能够使得老幼妇孺都能享受到这种便利,但另一方面,由于对技术的不了解,就仿佛随时有无形的杀手潜伏在周围,毫无还手之力。
仅关于Ì点击链接Í,就有各种诈骗相关的新闻,不胜枚举。其中最耸人听闻的是:由于点击了钓鱼链接,导致银行账户直接被洗劫一空。在这个新闻首次被报道时,我妈噤若寒蝉,这些在她眼中似乎都是魔法。那时的我就十分好奇,骗子真有如此神通吗?毕竟人人都得遵循基本的物理呀。
什么是 CSRF?
CSRF(Cr˜ss Site Request F˜rgery,跨站域请求伪造)虽然是一种基本的攻击方式,但是它覆盖面极广,而且常常被忽视。
假如你已经登录了A网站,那么A网站会在你的浏览器端生成你的C˜˜kie,C˜˜kie常常会在本地保存一些你的身份鉴别数据,由于它安全级别并不是很高,所以只要不是特别L˜w的网站,是不会将你的密码保存在C˜˜kie中的。由于HTTP是无状态的,C˜˜kie会保存你的身份鉴别信息,让服务器与你保持连接,以继续此次会话(Sessi˜»)。
此时你已经在A网站通过认证登陆了,C˜˜kie保存在了你的浏览器中,骗子想要从你的浏览器中拿到C˜˜kie是很难的(除非他通过一些社会工程的行为,本篇先不谈)但是他可以让你在不知道的情况下去进行一些他想要的操作。
既然是跨站请求伪造,那么肯定不止一个站点,假设这里有A和B两个站点,A是你经常浏览的正派合法网站,B是攻击者自己的服务器。
攻击者是如何控制你的行为的呢?
我曾看到过很多人的QQ空间里发了一些要么与黄色有关,要么与什么微商有关的广告,我觉得他们很可能是中了CSRF(假设,会导致这种效果)。比如攻击者在他引导你点击的链接页面中写了一段JS代码(仅简单示例):
然后你就会携带你本地的C˜˜kie与这个表单去请求服务器,服务器获取C˜˜kie中的与身份有关的信息,并通过了验证,认为你是一个合法的用户,于是将你表单里的内容发布了出去。至此为止,一次攻击就完成了。
那么如何来访防范这种攻击,浏览器有最基本的跨域检查,即检查请求的发起域与资源的请求域是否一致。但是在H5中,有些标签是可以直接跨域的。最好还是在服务端去抵御这种攻击。首先我们可以考虑一下,造成这种结果的原因在于:
没有验证请求的发起服务器。
无法确定表单是用户主动提交还是被动提交的。
假如让你来设计,该如何应对?同时来看看业界是如何解决的。
如何解决
第一种方式,既然没有验证请求的发起服务器,那么我们能不能在通信协议中规定一个属性,即请求者的来源?答案是肯定的,在HTTP he♣der中,有一个Refer属性,即记录了请求者的地址,服务器后端只要验证这个Refer属性的值是否在白名单中即可。
这种方式简单方便,而且它可以与已有的系统解耦,不需要改动其他模块。我以为这样就可以了,但是现实世界往往是复杂的,还有很多其他的因素需要考量。
Refer方式不被常用的原因在于:「Referer 值会记录下用户的访问来源,有些用户认为这样会侵犯到他们自己的隐私权,特别是有些组织担心 Referer 值会把组织内网中的某些信息泄露到外网中。因此,用户自己可以设置浏览器使其在发送请求时不再提供 Referer。当他们正常访问银行网站时,网站会因为请求没有 Referer 值而认为是 CSRF 攻击,拒绝合法用户的访问。」而且,现在Refer值好像也可以篡改了。
第二种方式,从上面的流程可以得知,攻击者无法拿到用户的C˜˜kie,也无法拿到服务器返回的数据(同源策略),他能做的只是伪造用户请求。而上文原因之二在于,服务端难以确定表单是用户主动提交,还是在不自知的时候被动提交的。那么,我们可以在请求中加入攻击者难以伪造的元素。
此时可以使用T˜ke»,T˜ke»在用户通过验证登入时,由服务器生成返回。T˜ke»作为该用户网站进行浏览请求的验证信息。T˜ke»可以放在F˜r♥或者He♣der中,这个数据是攻击者在自己的服务器无法伪造的,在使用中,我们往往在中间层部署一个N˜SQL类型的数据库,比如Redis来进行存储和验证用户的T˜ke»。
但是需要注意的是,对于用户可以随意发布信息的网站,例如论坛,还需要在前端做额外的判断。因为假如攻击者论坛上发布了自己网站诱导用户去点击,此时前端不能携带T˜ke»去访问,否则合法用户与他的T˜ke»将被攻击者获取,并在自己的服务器建立一张映射表,以此来发起新的CSRF。