Sparklr网站也是一脸茫然的样子,因为在它看来,atv,自己收到的授权请求,以及后续的令牌申请请求都是正常的,或者说它无法得知接收到的这些请求之间的关联关系,而且也无法区别出这些请求到底是来自张三本人,还是由李四伪造出来的。因此只要自己收到的参数是正确有效的,那就提供正常的认证服务,仅此而已。 2.5 攻击者李四视角 李四伪造了一个用户授权成功的请求,并且将其中的Authorization Code参数替换成了自己提前获取到的code。这样,当受害者张三的浏览器被欺骗从而发起令牌申请请求时,实际上是在用张三在Tonr网站上的账号和李四在Sparklr网站上的账号做绑定。 攻击完成后,李四在Tonr网站上可以通过自己在Sparklr网站的账号进行登录,而且登录进入的是张三在Tonr网站上的账号。而张三通过自己在Tonr网站上的账号登录进去之后,看到的是李四在Sparklr网站上的数据。 2.6 上帝视角 从整体上来看,这次攻击的时序图应该是下面这个样子的: 图3:攻击时序图示 3. 漏洞的本质 这个问题的关键点在于,OAuth2的认证流程是分为好几步来完成的,在图1中的第4步,第三方应用在收到一个GET请求时,除了能知道当前用户的cookie,以及URL中的Authorization Code之外,atv,难以分辨出这个请求到底是用户本人的意愿,还是攻击者利用用户的身份伪造出来的请求。 于是乎,攻击者就能使用移花接木的手段,提前准备一个含有自己的Authorization Code的请求,并让受害者的浏览器来接着完成后续的令牌申请流程。 4. 前提条件 尽管这个攻击既巧妙又隐蔽,但是要成功进行这样的CSRF攻击也是需要满足一定前提条件的。 首先,在攻击过程中,受害者张三在Tonr网站上的用户会话(User Session)必须是有效的,也就是说,张三在受到攻击前已经登录了Tonr网站。 其次,整个攻击必须在短时间内完成,因为OAuth2提供者颁发的Authorization Code有效期很短,OAuth2官方推荐的时间是不大于10分钟,而一旦Authorization Code过期那么后续的攻击也就不能进行下去了。 最后,一个Authorization Code只能被使用一次,如果OAuth2提供者收到重复的Authorization Code,它会拒绝当前的令牌申请请求。不止如此,根据OAuth2官方推荐,它还可以把和这个已经使用过的Authorization Code相关联的access_token全部撤销掉,进一步降低安全风险。 5. 防御办法 要防止这样的攻击其实很容易,作为第三方应用的开发者,只需在OAuth认证过程中加入state参数,并验证它的参数值即可。具体细节如下: 在将用户重定向到OAuth2的Authorization Endpoint去的时候,为用户生成一个随机的字符串,并作为state参数加入到URL中。 在收到OAuth2服务提供者返回的Authorization Code请求的时候,验证接收到的state参数值。如果是正确合法的请求,那么此时接受到的参数值应该和上一步提到的为该用户生成的state参数值完全一致,否则就是异常请求。 state参数值需要具备下面几个特性: * 不可预测性:足够的随机,使得攻击者难以猜到正确的参数值关联性:state参数值和当前用户会话(user session)是相互关联的 唯一性:每个用户,甚至每次请求生成的state参数值都是唯一的 时效性:state参数一旦被使用则立即失效 6. 总结 要避免遭受本文提到的CSRF攻击问题,需要第三方应用正确的使用state参数,然而纵观各大OAuth服务提供者,在其开发文档里都没有明确把state参数和CSRF攻击联系起来,仅仅只是像下面这样一句话带过: “参数:state 是否必须:否 说明:重定向后会带上state参数,开发者可以填写a-zA-Z0-9的参数值,最多128字节” 让事情变得更糟糕的是,state是可选参数,因此更容易被开发者忽略,造成安全风险。此外,本文中的攻击非常巧妙,可以悄无声息的攻陷受害者的账号,难以被察觉到。 作为第三方应用的开发者,我们除了参考OAuth2服务提供者的开发文档之外,还应当加深自己对OAuth2的理解,尽可能的避开这些安全的坑。 而作为OAuth2服务提供者,也应当承担起提醒开发者注意防范安全风险的责任。 参考资料 OAuth2 Simplified OAuth2 Authorization Framework RFC6749 (责任编辑:本港台直播) |