0x00 前言
最近OAuth又出了一个漏洞,根据漏洞介绍这个漏洞不在OAuth协议,而在用户的部署上,等详细分析吧,漏洞链接如下,借此机会复习一下OAuth2.0
http://www.cnvd.org.cn/flaw/show/CNVD-2018-01622
0x01 OAuth 2.0 流程
详细的交换就不写了,网上一大堆,简单总结两句授权码模式(authorization code),包括了(用户A,网站B,GitHub(信息提供者,resource owner))
前提:网站B要想得到GitHub认证,必须先和GitHub协商可以取得什么权限等事情,这时候GitHub认可了网站B的合法性并同意网站B可以使用GitHub的相关用户资料,并给了网站B:Client Id 和 Client Secret。
用户A想登陆网站B,但不想注册,所以点击了下面的GitHub图标,想通过GitHub账号登陆网站B,这时候就触发了OAuth认证
网站带着GitHub颁发的Client Id 到 ”https://github.com/login/oauth/authorize“ 请求权限,这时候会显示网站B从GitHub会取得什么权限,如果用户觉得ok就点击授权,不ok就拒绝,认证结束
GET /authorize?response_type=code&client_id=s6BhdRkqt3&redirect_uri=https%3A%2F%2Fclient%2Eexample%2Ecom%2Fcb HTTP/1.1 |
- 当如何点击授权后,页面会跳转到网站B预先设定的 redirect_uri 并附带一个授权码(code)
HTTP/1.1 302 Found |
- 用户A的浏览器得到一个302的回复,然后重定向到网站B指定redirect_uri,网站B从而得到了code,然后网站B拿着 code 和能够标识个人身份的 “client_id “,client_secret“ 去拜访 github,拿到access_token,这里的“client_id “,client_secret“ 是Github判断这个请求的合法性依据,”,client_secret“这个是网站B独有的。
POST //github.com/login/oauth/access_token |
- 从上面的response中得到了access_token,相应的scope,网站B可以凭借这个token来取得用户在GitHub的信息,至于能够取得的信息范围就由scope来定义了,用户A可以使用GitHub的账号登陆网站B了。这就是授权码模式的简单描述。
0x02 已知的安全问题-CSRF
- 漏洞原理
这个问题的关键点在于,OAuth2的认证流程是分为好几步来完成的,在图1中的第4步,第三方应用在收到一个GET请求时,除了能知道当前用户的cookie,以及URL中的Authorization Code之外,难以分辨出这个请求到底是用户本人的意愿,还是攻击者利用用户的身份伪造出来的请求。 于是乎,攻击者就能使用移花接木的手段,提前准备一个含有自己的Authorization Code的请求,并让受害者的浏览器来接着完成后续的令牌申请流程。(知乎转载)
- 详细分析
回顾一下上面简单总结的第四步:
用户A的浏览器得到一个302的回复,然后重定向到网站B指定redirect_uri,网站B从而得到了code,然后网站B拿着 code 和能够标识个人身份的 “client_id “,client_secret“ 去拜访 github,拿到access_token,这里的“client_id “,client_secret“ 是Github判断这个请求的合法性依据,”,client_secret“这个是网站B独有的。
这里面有个问题就是用户A的浏览器根据302状态码把code redirect到网站B,这个code是和将要登录网站B的账号关联的,如果攻击者替换了用户A这个请求,把里面的code换成攻击者账号的,然后网站B继续完成OAuth的认证流程,这会造成用户A在网站B的账号和攻击者的GitHub账号绑定了,攻击者只要在网站B通过GitHub登录就能成功登录用户A在网站B的账号了。因此这个漏洞本质是欺骗网站B
- 攻击成功条件
- 用户A的User Session是valid的
- OAuth2提供者颁发的Authorization Code有效期很短,OAuth2官方推荐的时间是不大于10分钟,所以要快
- 一个Authorization Code只能被使用一次
0x03 如何防御
刚才提到了,既然这个漏洞本质是欺骗网站B,那么要做防御的当然在网站B了,网站B要确保用户发去GitHub的授权码(Authorization Code)申请和用户redirect回来的授权码是一致的,怎么做呢?就是加一个唯一的,随机的参数state来确保唯一性
- 首先,网站B redirect 用户A到GitHub申请授权码的时候带上”state=xyz“参数
GET /authorize?response_type=code&client_id=s6BhdRkqt3&state=xyz |
- 然后,用户A的浏览器收到GitHub发来的302带着授权码redirect回到B网站时,GitHub会根据申请code时的请求在302的location中带上”state=xyz“
HTTP/1.1 302 Found |
这样网站B就可以根据state参数确认这个code对应哪一个用户了
要避免遭受本文提到的CSRF攻击问题,需要第三方应用正确的使用state参数
Refer link
http://www.ruanyifeng.com/blog/2014/05/oauth_2_0.html