本鱼拟成立工作室承接项目开发/软件定制/云设施开发运维/办公设备技术支持等,如您有相关需求,欢迎来询 | ::博客文章推荐::

闲聊下XSS和CSRF的东东

: WEB前端 木魚 4907℃ 1评论

以前有在玩QQ应用的摩天大楼,可是话说现在开始觉得太花时间了,就想着怎么样能继续不浪费以前花的时间也能节省以后的时间。这不,就想到了自己写个外挂来帮我玩。

可是冒出这个念头的时候吓了一跳,一直自我标榜从来不用外挂玩游戏的,竟然会有这个念头。可是转念一想如果真要摊开说的话,任何用新技术来实现自动化的方案不都是在玩“外挂”了吗?可见外挂不外挂的,完全是看用的人:如果是为了破坏公平性为了让自己不劳而获的,那就是恶意的。如果从节约自己时间和精力的角度出发,似乎是可以被人接受的。

嗯。上面都是闲话,下面说正事儿。

 

(1) 由来

下午我在研究QQ空间如何从一个AppID(应用ID)来获得OpenKey和OpenID,由于第三方应用是无法获得QQ帐号本身的信息的(包括密码啥的),所以只好由腾讯方提供开放的OpenID和OpenKey来标识身份,第三方应用程序商拿着这俩参数去和腾讯交换必备的信息(如用户基本信息、用户好友等)。听起来这很像OAuth的作法,嗯,基本原理是类似的。

话说腾讯的QQ空间完全就是个Javascript堆起来的罐子,到处是js。在Chrome的开发人员工具里面调试了半天,终于摸清了点击左侧的应用菜单后的跳转流程。

在点击菜单后,js会先用ajax的手段拿着AppID去请求OpenID和OpenKey,以摩天大楼为例,AppID为600,请求回OpenID和OpenKey后生成大楼的链接,显示在框架中就行了。

这个获得OpenID和OpenKey的链接格式类似这样:

http://r.cnc.qzone.qq.com/cgi-bin/qzapp/userapp_getopeninfo.cgi?uin=286495995&r=0.7878445323&g_tk=152454817

上面这个地址中,最开始的地址是页面处理路径,而加粗的两个参数则是关键的。uin是当前的用户ID,g_tk则是CSRF的验证参数。r么,防止浏览器缓存的参数。

g_tk参数既是本文主角。

 

(2) 先提一下XSS

以前我听说过XSS攻击,而CSRF则没怎么留意。

所谓XSS攻击,又叫跨站脚本攻击,想了解的同学可以在百度百科里面了解一下。简单来说,就是在一些过滤不够严格而存在漏洞的站点上,通过精心构造的内容,导致用户在浏览相应页面的时候,恶意构造的代码或脚本得以执行,从而对用户造成攻击。

常见的手段包括,针对用户的通过XSS嵌入框架、脚本盗窃信息或试图由挂马的网页使用户最终中毒的,针对系统的通过构造的内容篡改、破坏文件或获得相应管理员权限。

 

(3) 再说说CSRF

所谓的CSRF,又叫跨站伪造请求攻击,同样百度百科里面也有关于它的资料,不过在寡人看来资料较为粗犷不够翔实,也比较乱。

 

所谓的跨站伪造请求攻击,有两个前提因素:

1.用户已经登录目标站点,或系统保存有目标站点的登录信息(也就是用户不需要再登录即可直接进行操作);

2.目标的操作页面可以使用GET或POST方式提交,并没有CSRF防护措施,也并没有对来源进行任何校验;

 

相信很多人和我一样,特别懒得每次去一个网站时都手动登录一下,大部分情况下登录的时候除非是在公共场合,我都会选择“记住登录信息”这个选项;此时第一个条件就具备了。就算没有这样的“坏习惯”,在使用现代的多窗口浏览器(大多数浏览器都是了),只要在打开企图CSRF攻击的站点之前已经打开过目标站点并且没有退出浏览器,那么这个登录信息也是具备的。

以论坛为例,假定我登录了A论坛,登录的时候选择了记住密码,或者我在打开企图实行CSRF攻击的B站点之前光顾过A论坛。第1个条件满足。

 

假定在A论坛上有个页面是logout.aspx,这个页面的作用就是让我退出登录。那么,如果B站点想要利用它来实行CSRF攻击,它可以在页面上放上这样的代码:

  1. <img src="http://A/logout.aspx" />

当我们浏览这个页面的时候,这里会显示一个叉叉,因为它不是图片地址。不过这不是关键,关键是,当浏览器在显示到这里的时候,它会以为这确实是一个图片,然后老老实实地到 http://A/logout.aspx 这里去加载图片(也就是发起请求)。由于目标地址是A论坛的,所以浏览器会附带上之前提到的身份验证信息(记住的密码或者类似的东西),然后,我们再回到A论坛上的时候,就会发现自己已经被莫名其妙地注销了——因为之前浏览器已经发起了注销的请求,论坛会以为是用户主动要求注销的。

这就构成了一个最简单的CSRF攻击。不过貌似它除了有点儿恶搞的味道外,并没有任何危害。

 

其实仔细想想,会发现CSRF攻击已经被很多人玩过了。最普及的莫过于论坛了,经常有人发个贴子说你进来看呀进来看呀,进去后啥也没看到,出来就发现自己被注销了。这就是最简单的CSRF攻击了,只是大家都只当是玩笑而已。

复杂的CSRF攻击可以使用表单来实现,比如嵌入一个隐藏的框架放个表单指向A论坛的资料修改页面,当你浏览页面的时候就自动提交,此时在你正在看B站点页面之时其实你在A论坛上的个人资料已经被篡改了。

 

(4) 如何防止CSRF

要防止CSRF攻击,有好几种方案,例如:

  1. 目标操作页面的时候,需要提交原始密码(也就是需要再次验证),这通常是比较重要的操作才需要的,比如网银操作、修改密码等等;
  2. 目标页面会对请求的来源进行限制,比如限制页面的Referrer(引用页),或带上操作的验证码。相比而言够用了,但有局限性,比如当子站点非常多的时候,或是公共的服务页面(比如是API函数接口等),无法预知所有的来源,就难以实行了;
  3. 在操作的时候带上防止CSRF的标记(也就是进行二次验证),这比较适合Ajax操作,因为需要有客户端脚本的支持,利用的是跨域无法读取Cookies的安全性设置。
今天说到的腾讯的防止CSRF方案,就是上面的第三条,在它的Ajax请求中的CSRF防止方案。
 
(5) QQ空间的Ajax防CSRF是怎么操作的?
QQ空间的AJAX请求大多是Json格式的数据交换,有个JSONGetter专门负责此类的数据请求。JSONGetter进行最终的请求前,会调用 QZONE.FronePage.getASCRFToken() 函数来获得 Anti-CSRF Token(字面解释,防止CSRF攻击的密钥)。
这部分函数如下(位于 interface.js 中):
 
QZONE.FrontPage.getACSRFToken = function() {
    return arguments.callee._DJB(QZFL.cookie.get("skey"));
};
QZONE.FrontPage.getACSRFToken._DJB = function(str) {
    var hash = 5381;
    for (var i = 0, len = str.length; i < len; ++i) {
        hash += (hash << 5) + str.charAt(i).charCodeAt();
    }
    return hash & 0x7fffffff;
};
从字面上看,机理并不复杂,就是取出客户端名为skey的Cookie进行运算后得到一个新的数字而已。由于跨域安全性的设置,第三方域名无法读取skey这样的Cookies,从而使第三方无法获得这个ASCRFToken。
(skey类似于SessionID,用于标识在线用户唯一身份的字符串)
 
(6) 其它
不过腾讯的CSRF校验并不严格。有些相同的服务,只是因为在不同的服务器上部署的,竟然有的有验证有的没有……比如第一节中提到的那个请求,当使用 base.qzone.qq.com 服务器时,就不需要ACSRF Token,当然它们的返回数据是有点儿区别的,不过非常细微,几乎可以忽略。这可能涉及到服务器端程序的部署版本区别。

本日志备份自 QQ 空间,原文地址:http://user.qzone.qq.com/286495995/blog/1308572198

喜欢 (7)
发表我的评论
取消评论
表情

Hi,您需要填写昵称和邮箱!

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址
(1)个小伙伴在吐槽
  1. 消灭 0 回复

    凉粉2019-08-21 10:29 回复