在开发 OAuth认证服务器 的时候,开发者的安全意识不高的话,很可能会忽略 state 参数,从而导致 出现 csrf 漏洞。
但是在说明这个state参数前,有必要了解大部分程序员所写的绑定OAuth账号流程,由于绑定流程很多,这里挑最常见的“用户在第三方网站A上登录后,通过Authorization code方式绑定微博流程(也是这个漏洞常见的场景流程):
1、用户甲到第三方网站A登录后,到了绑定页面。此时还没绑定微博。绑定页面提供一个按钮:“绑定微博”(地址a: http://aaa.com/index.php?m=user_3rd_bind_sina )
2、用户甲点击地址a,程序生成如下地址b(为方便大家查看,参数部分均未urlencode以【】包含显示): https://api.weibo.com/oauth2/authorize?client_id=【9999999】&redirect_uri=【http://aaa.comindex.php?m=user_3rd_bind_sina_callback】&response_type=【code】
3、用户甲浏览器定向到地址b,授权该应用。
4、授权服务器根据传递的redirect_uri参数,组合认证参数code生成地址c: http://aaa.comindex.php?m=user_3rd_bind_sina_callback&code=【809ui0asduve】
5、用户甲浏览器返回到地址c,完成绑定。
咋看起来,好像没啥问题,毕竟code也是不可预测嘛。但是各开发者有没有想过,地址c实质上是和当前登录用户一点关系都没有的,因为地址c只能证明微博用户信息,但无法证明网站A的用户信息。所以漏洞就此产生了——假设有用户乙和丙,同时发起绑定请求,登录到不同的微博账号,然后在第5步后打住,互相交换地址c,会是什么结果?答案就是用户乙绑定了用户丙的微博,用户丙绑定了用户乙的微博了…...攻击者的目标,其实就是要获取地址c,然后诱骗已登录网站A的受害者点击,从而改变了绑定关系。
为应对这种情况,Oauth 2.0引入了state参数。这个参数在许多开放平台上也会有提及,比如新浪微博的 Oauth2/authorize(http://open.weibo.com/wiki/Oauth2/authorize ): 用于保持请求和回调的状态,在回调时,会在Query Parameter中回传该参数。开发者可以用这个参数验证请求有效性,也可以记录用户请求授权页前的位置。这个参数可用于防止跨站请求伪造(CSRF)攻击。然而大多数开发者(包括许多官方SDK),会忽略使用这个state参数。所以,大面积的网站(不乏大站)确实存在这种漏洞。
但是利用条件也有点苛刻。
攻击者必须了解第三方网站可能的绑定特性,否则攻击极可能失败。绝大多数网站都是一个网站账号只能绑定一个OAuth提供方账号(比如微博帐号)。这种特性,导致这个漏洞在绝大多数网站根本无法快速撒网,只能定向劫持未绑定的用户到攻击者OAuth账号上,而攻击一次后,这个账号必须解绑才能用于别的攻击,导致利用难度增大不少。
攻击者还必须了解被定向劫持用户的上网习惯。在这个漏洞中,绝大部分都需要受害者在第三方网站上处于登录状态,否则攻击基本失效。
攻击者还必须了解第三方网站、以及用户在该第三方网站上存在的利益。现在的攻击有许多都是带有利益的,如果是电商类的话,网站本身的金钱利益驱动可能存在,但又需要判断该用户在该网站是否存在高价值,这就增加了额外的工作量;如果只是娱乐类网站,除了言论相关和用户所拥有的网站管理权,我还想不出有什么可以吸引攻击者去定向攻击。
以上各种条件造就了在攻击实施环节更像是那种一对一的淘宝或者QQ钓鱼手段、或者放入针对高价值目标的社工(或APT)一环中。就前者而言,淘宝、QQ甚至各类热门游戏的钓鱼量之大,安全研究者们应该更清楚;就后者而言,实质还有其它更有效地手段。那么,这个漏洞,还能冠以“最大规模帐号劫持”吗?我相信,地下产业者看到后只会轻蔑的笑一下,然后继续埋头干活
对于开发者而言,要修复这个漏洞,就是必须加入state参数,这个参数既不可预测,又必须可以充分证明client和当前第三方网站的登录认证状态存在关联(如果存在过期时间更好)。其实,随机算一个字符串,然后保存在session,回调时检查state参数和session里面的值,就满足要求了。