- A+
一、前言
半个月前, @Tater 发了一个《透过[新浪微博]官方来源调用API发表微博.无需client_sec》(WooYun: 透过[新浪微博]官方来源调用API发表微博.无需client_sec ),但是对于其中涉及的OAuth 2.0的缺陷语焉不详。这几天趁着另一个项目的迁移机会对其它国内支持OAuth 2.0开放平台进行了相关测试,遂有此文。为了各位翻阅相关文档方便,部分英文词汇仅作注释。
二、OAuth 1.0:app secret不能泄露
OAuth 1.0的设计偏向于server-side(服务器端)的调用方式,因而特别强调访问受保护资源都必须依赖成对的token凭据(the token credentials,即Access Token和Access Token Secret)和成对的client凭据(the client credentials,即常说的应用app key和app secret)共同进行签名和提供校验,才能访问成功。比如新浪微博V1接口调用用户信息API的请求方式如下:
GET /users/show/123456.json?oauth_consumer_key=【应用APP KEY】&oauth_nonce=【OAuth随机值】&oauth_signature=【查询参数+应用APP SECRET+Access Token Secret三者共同进行签名后的值】&oauth_signature_method=HMAC-SHA1&oauth_timestamp=【请求时间】&oauth_token=【Access Token】&oauth_version=1.0a HTTP/1.1
Host: api.t.sina.com.cn
因此,就非常纯粹的接口来源文案攻击来讲,攻击者必须要同时知道app key和app secret才能攻击成功。由于client凭据中的app key是公开的,故而所有开放平台都明确申明,client凭据中的app secret是不能泄露的——虽然在客户端来讲,由于反编译、反汇编或内存调试等技术的存在使得这个假设实质无效(一个例子就是结合WooYun: CSRF导致微博应用自动授权 中的授权页面CSRF,从而进行各种手机客户端非法授权,weico是最惨的受害者之一);但是针对纯粹的网站应用或其他不能获知代码或汇编的应用来讲,这是可以而且必须要做到的,实际上也感觉这类应用较少出现这类攻击(除非服务端源代码出现了泄露、或者被社工获取)。
三、OAuth 2.0:被抛弃的双secret
而到了OAuth 2.0时代,由于要实现client-side(客户端;比如手机客户端、jssdk等)的调用方式,而前面也得知,客户端实际上保密不了双secret(app secret,access token secret),因此协议设计者干脆将访问受保护资源中的步骤中,把双secret抛弃掉了,尤其在访问受保护资源时,只需要access token、无需使用双secret加密或者校验即可完成访问(类似cookies)。这就是Eran Hammer在《OAuth 2.0:通往地狱之路》(http://zone.wooyun.org/content/674 )所说的“无绑定token”(Unbounded tokens)。
在国内实际的平台建设情况中,实施情况和提案(http://tools.ietf.org/html/draft-ietf-oauth-v2-31 )大同小异,当然也略有差别:
(1)即便是server-side应用,在访问受保护资源时,app secret也基本被抛弃,基本只需提供access token即可访问受保护资源;不过,有些平台或接口则需要同时提供app key参数,视乎其安全考虑情况。
以上可简单归结为由于缺失了双secret的签名,api基本难以分辨请求来源。
(2)通过client-side Implicit Grant方式认证获取的access token可用于server-side的通讯中。
(3)各开放平台由于要实施client-side应用的Implicit Grant方式,对用于从fragment_id获取access token的授权中转页面或者跨域文件大开绿灯(形如http://xxx/oauth2/success.html#access_token=2YotnFZFEjr1zCsicMWpAA&state=xyz&token_type=example&expires_in=3600)。
(4)根据提案,client-side Implicit Grant方式认证无需app secret参与,只需要app key。
于是乎攻击者突然感到前途一片光亮:对于网站应用来讲,app key是必定公开的,而client-side Implicit Grant方式认证获取的access token又可以用于server-side的通讯中,那么在不知道app secert下针对网站应用的攻击,就成为现实。
而另一方面,由于api事实上难以分辨请求来源,那么只要掌握了A应用中受害者的access token,并且在B应用中的某些关键请求步骤中替换掉,那么就有可能顺利登录到B应用的受害者账号,从而引发其它不良后果。
四、现实案例
@Tater 发了新浪微博的案例,这里不详述:WooYun: 透过[新浪微博]官方来源调用API发表微博.无需client_sec
我则验证了QQ登录,也发现了类似情况,见:
WooYun: 无需client_sec可用QQ登录平台发表空间日志等高权限操作(WooYun-2012-11314衍生)
至于人人,感觉摸不着头脑,虽然在OAuth 2.0 server-side模式下保留了需要参数签名才可调用,不过有些应用还是可以乱填app secret而成功调用高权限接口(比如优酷),而有些应用就不成(比如糯米),老报“sig错误”,原因未知。
关于用A应用access token登录到B应用的受害者账号中,请参见今年6月微软
研究组发给IETF的信件:http://www.ietf.org/mail-archive/web/oauth/current/msg09270.html
五、可能的影响和想到的缓解方法
OAuth 2.0的这种认证缺陷,能否造成CSRF是流行关键之一,不过貌似由于来源的局限性问题,导致难以流行(除非出现了官方应用那种)。就现在而言,想到的似乎只能便宜两种人:
(1)拥有大量被盗账号的地下产业者。他们终于拥有了更宽广且更有效的针对高权限来源的攻击,从前只能苦苦追寻客户端类别,如今可以轻松扩展到网站类别了。
(2)拥有大量access token的开发者或地下产业者。不过access token的时效性不算高,而且一般不会提供refresh token,一定程度上遏制了这个问题。
网站应用如何缓解这个client-side导致的access token攻击问题?在app key一定是公开的现实下,目前而言似乎没找到什么合适的法子,只能寄希望于开放平台的安全监控系统。
而至于“用A应用access token登录到B应用的受害者账号中”问题,缓解方法是各应用除了验证access token之外,还必须辅助其他参数进行判断(比如自行加入其它认证参数进行双重认证);另一种方法则是验证access token背后所属的应用app key等唯一性和对应性(无论是自行验证还是开放平台通过签名等形式帮助验证),确保该access token是该应用的。有点可惜的是,开放平台基本对此不支持——虽然这貌似怪不了平台,只能怪OAuth 2.0协议是个框架不明确,看看现在提案走到第31版就知道了……