【转】OAuth的改变
原文地址:http://huoding.com/2011/11/08/126
去年我写过一篇《OAuth那些事儿》,对OAuth做了一些简单扼要的介绍,今天我打算写一些细节,以阐明OAuth如何从1.0改变成1.0a,继而改变成2.0的。
OAuth1.0
在OAuth诞生前,Web安全方面的标准协议只有OpenID,不过它关注的是验证,即WHO的问题,而不是授权,即WHAT的问题。好在FlickrAuth和GoogleAuthSub等私有协议在授权方面做了不少有益的尝试,从而为OAuth的诞生奠定了基础。
OAuth1.0定义了三种角色:User、Service Provider、Consumer。如何理解?假设我们做了一个SNS,它有一个功能,可以让会员把他们在Google上的联系人导入到SNS上,那么此时的会员是User,Google是Service Providere,而SNS则是Consumer。
+----------+ +----------+
| |--(A)- Obtaining a Request Token --------->| |
| | | |
| |<-(B)- Request Token ----------------------| |
| | (Unauthorized) | |
| | | |
| | +--------+ | |
| |>-(C)-| -+-(C)- Directing ---------->| |
| | | -+-(D)- User authenticates ->| |
| | | | +----------+ | Service |
| Consumer | | User- | | | | Provider |
| | | Agent -+-(D)->| User | | |
| | | | | | | |
| | | | +----------+ | |
| |<-(E)-| -+-(E)- Request Token ------<| |
| | +--------+ (Authorized) | |
| | | |
| |--(F)- Obtaining a Access Token ---------->| |
| | | |
| |<-(G)- Access Token -----------------------| |
+----------+ +----------+
花絮:OAuth1.0的RFC没有ASCII流程图,于是我敲了几百下键盘自己画了一个,后经网友提示,Emacs可以很轻松的搞定ASCII图:Emacs Screencast: Artist Mode,VIM当然也可以搞定,不过要借助一个插件:DrawIt,可惜我的键盘都要坏了。
Consumer申请Request Token(/oauth/1.0/request_token):
oauth_consumer_key
oauth_signature_method
oauth_signature
oauth_timestamp
oauth_nonce
oauth_version
Service Provider返回Request Token:
oauth_token
oauth_token_secret
Consumer重定向User到Service Provider(/oauth/1.0/authorize):
oauth_token
oauth_callback
Service Provider在用户授权后重定向User到Consumer:
oauth_token
Consumer申请Access Token(/oauth/1.0/access_token):
oauth_consumer_key
oauth_token
oauth_signature_method
oauth_signature
oauth_timestamp
oauth_nonce
oauth_version
Service Provider返回Access Token:
oauth_token
oauth_token_secret
…
注:整个操作流程中,需要注意涉及两种Token,分别是Request Token和Access Token,其中Request Token又涉及两种状态,分别是未授权和已授权。
OAuth1.0a
OAuth1.0存在安全漏洞,详细介绍:Explaining the OAuth Session Fixation Attack,还有这篇:How the OAuth Security Battle Was Won, Open Web Style。
简单点来说,这是一种会话固化攻击,和常见的会话劫持攻击不同的是,在会话固化攻击中,攻击者会初始化一个合法的会话,然后诱使用户在这个会话上完成后续操作,从而达到攻击的目的。反映到OAuth1.0上,攻击者会先申请Request Token,然后诱使用户授权这个Request Token,接着针对回调地址的使用,又存在以下几种攻击手段:
- 如果Service Provider没有限制回调地址(应用设置没有限定根域名一致),那么攻击者可以把oauth_callback设置成成自己的URL,当User完成授权后,通过这个URL自然就能拿到User的Access Token。
- 如果Consumer不使用回调地址(桌面或手机程序),而是通过User手动拷贝粘贴Request Token完成授权的话,那么就存在一个竞争关系,只要攻击者在User授权后,抢在User前面发起请求,就能拿到User的Access Token。
为了修复安全问题,OAuth1.0a出现了(RFC5849),主要修改了以下细节:
- Consumer申请Request Token时,必须传递oauth_callback,而Consumer申请Access Token时,不需要传递oauth_callback。通过前置oauth_callback的传递时机,让oauth_callback参与签名,从而避免攻击者假冒oauth_callback。
- Service Provider获得User授权后重定向User到Consumer时,返回oauth_verifier,它会被用在Consumer申请Access Token的过程中。攻击者无法猜测它的值。
Consumer申请Request Token(/oauth/1.0a/request_token):
oauth_consumer_key
oauth_signature_method
oauth_signature
oauth_timestamp
oauth_nonce
oauth_version
oauth_callback
Service Provider返回Request Token:
oauth_token
oauth_token_secret
oauth_callback_confirmed
Consumer重定向User到Service Provider(/oauth/1.0a/authorize):
oauth_token
Service Provider在用户授权后重定向User到Consumer:
oauth_token
oauth_verifier
Consumer申请Access Token(/oauth/1.0a/access_token):
oauth_consumer_key
oauth_token
oauth_signature_method
oauth_signature
oauth_timestamp
oauth_nonce
oauth_version
oauth_verifier
Service Provider返回Access Token:
oauth_token
oauth_token_secret
注:Service Provider返回Request Token时,附带返回的oauth_callback_confirmed是为了说明Service Provider是否支持OAuth1.0a版本。
…
签名参数中,oauth_timestamp表示客户端发起请求的时间,如未验证会带来安全问题。
在探讨oauth_timestamp之前,先聊聊oauth_nonce,它是用来防止重放攻击的,Service Provider应该验证唯一性,不过保存所有的oauth_nonce并不现实,所以一般只保存一段时间(比如最近一小时)内的数据。
如果不验证oauth_timestamp,那么一旦攻击者拦截到某个请求后,只要等到限定时间到了,oauth_nonce再次生效后就可以把请求原样重发,签名自然也能通过,完全是一个合法请求,所以说Service Provider必须验证oauth_timestamp和系统时钟的偏差是否在可接受范围内(比如十分钟),如此才能彻底杜绝重放攻击。
…
需要单独说一下桌面或手机应用应该如何使用OAuth1.0a。此类应用通常没有服务端,无法设置Web形式的oauth_callback地址,此时应该把它设置成oob(out-of-band),当用户选择授权后,Service Provider在页面上显示PIN码(也就是oauth_verifier),并引导用户把它粘贴到应用里完成授权。
一个问题是应用如何打开用户授权页面呢?很容易想到的做法是使用内嵌浏览器,说它是个错误的做法或许有点偏激,但它至少是个对用户不友好的做法,因为一旦浏览器内嵌到程序里,那么用户输入的用户名密码就有被监听的可能;对用户友好的做法应该是打开新窗口,弹出系统默认的浏览器,让用户在可信赖的上下文环境中完成授权流程。
不过这样的方式需要用户在浏览器和应用间手动切换,才能完成授权流程,某种程度上说,影响了用户体验,好在可以通过一些其它的技巧来规避这个问题,其中一个行之有效的办法是Monitor web-browser title-bar,简单点说,操作系统一般提供相应的API可以让应用监听桌面上所有窗口的标题,应用一旦发现某个窗口标题符合预定义格式,就可以认为它是我们要的PIN码,无需用户参与就可以完成授权流程。Google支持这种方式,并且有资料专门描述了细节:Auto-Detecting Approval(注:墙!)。
还有一点需要注意的是对桌面或移动应用来说,consumer_key和consumer_secret通常都是直接保存在应用里的,所以对攻击者而言,理论上可以通过反编译之类的手段解出来。进而通过consumer_key和consumer_secret签名一个伪造的请求,并且在请求中把oauth_callback设置成自己控制的URL,来骗取用户授权。为了屏蔽此类问题,Service Provider需要强制开发者必须预定义回调地址:如果预定义的回调地址是URL方式的,则需要验证请求中的回调地址和预定义的回调地址是否主域名一致;如果预定义的回调地址是oob方式的,则禁止请求以URL的方式回调。
OAuth2.0
OAuth1.0虽然在安全性上经过修补已经没有问题了,但还存在其它的缺点,其中最主要的莫过于以下两点:其一,签名逻辑过于复杂,对开发者不够友好;其二,授权流程太过单一,除了Web应用以外,对桌面、移动应用来说不够友好。
为了弥补这些短板,OAuth2.0做了以下改变:
首先,去掉签名,改用SSL(HTTPS)确保安全性,所有的token不再有对应的secret存在,这也直接导致OAuth2.0不兼容老版本。
其次,针对不同的情况使用不同的授权流程,和老版本只有一种授权流程相比,新版本提供了四种授权流程,可依据客观情况选择。
在详细说明授权流程之前,我们需要先了解一下OAuth2.0中的角色:
OAuth1.0定义了三种角色:User、Service Provider、Consumer。而OAuth2.0则定义了四种角色:Resource Owner、Resource Server、Client、Authorization Server:
- Resource Owner:User
- Resource Server:Service Provider
- Client:Consumer
- Authorization Server:Service Provider
也就是说,OAuth2.0把原本OAuth1.0里的Service Provider角色分拆成Resource Server和Authorization Server两个角色,在授权时交互的是Authorization Server,在请求资源时交互的是Resource Server,当然,有时候他们是合二为一的。
下面我们具体介绍一下OAuth2.0提供的四种授权流程:
Authorization Code
可用范围:此类型可用于有服务端的应用,是最贴近老版本的方式。
+----------+
| resource |
| owner |
| |
+----------+
^
|
(B)
+----|-----+ Client Identifier +---------------+
| -+----(A)-- & Redirection URI ---->| |
| User- | | Authorization |
| Agent -+----(B)-- User authenticates --->| Server |
| | | |
| -+----(C)-- Authorization Code ---<| |
+-|----|---+ +---------------+
| | ^ v
(A) (C) | |
| | | |
^ v | |
+---------+ | |
| |>---(D)-- Authorization Code ---------' |
| Client | & Redirection URI |
| | |
| |<---(E)----- Access Token -------------------'
+---------+ (w/ Optional Refresh Token)
Client向Authorization Server发出申请(/oauth/2.0/authorize):
response_type = code
client_id
redirect_uri
scope
state
Authorization Server在Resource Owner授权后给Client返回Authorization Code:
code
state
Client向Authorization Server发出申请(/oauth/2.0/token):
grant_type = authorization_code
code
client_id
client_secret
redirect_uri
Authorization Server在Resource Owner授权后给Client返回Access Token:
access_token
token_type
expires_in
refresh_token
说明:基本流程就是拿Authorization Code换Access Token。
Implicit Grant
可用范围:此类型可用于没有服务端的应用,比如Javascript应用。
+----------+
| Resource |
| Owner |
| |
+----------+
^
|
(B)
+----|-----+ Client Identifier +---------------+
| -+----(A)-- & Redirection URI --->| |
| User- | | Authorization |
| Agent -|----(B)-- User authenticates -->| Server |
| | | |
| |<---(C)--- Redirection URI ----<| |
| | with Access Token +---------------+
| | in Fragment
| | +---------------+
| |----(D)--- Redirection URI ---->| Web-Hosted |
| | without Fragment | Client |
| | | Resource |
| (F) |<---(E)------- Script ---------<| |
| | +---------------+
+-|--------+
| |
(A) (G) Access Token
| |
^ v
+---------+
| |
| Client |
| |
+---------+
Client向Authorization Server发出申请(/oauth/2.0/authorize):
response_type = token
client_id
redirect_uri
scope
state
Authorization Server在Resource Owner授权后给Client返回Access Token:
access_token
token_type
expires_in
scope
state
说明:没有服务端的应用,其信息只能保存在客户端,如果使用Authorization Code授权方式的话,无法保证client_secret的安全。BTW:不返回Refresh Token。
Resource Owner Password Credentials
可用范围:不管有无服务端,此类型都可用。
+----------+
| Resource |
| Owner |
| |
+----------+
v
| Resource Owner
(A) Password Credentials
|
v
+---------+ +---------------+
| |>--(B)---- Resource Owner ------->| |
| | Password Credentials | Authorization |
| Client | | Server |
| |<--(C)---- Access Token ---------<| |
| | (w/ Optional Refresh Token) | |
+---------+ +---------------+
Clien向Authorization Server发出申请(/oauth/2.0/token):
grant_type = password
username
password
scope
AuthorizationServer给Client返回AccessToken:
access_token
token_type
expires_in
refresh_token
说明:因为涉及用户名和密码,所以此授权类型仅适用于可信赖的应用。
Client Credentials
可用范围:不管有无服务端,此类型都可用。
+---------+ +---------------+
| | | |
| |>--(A)- Client Authentication --->| Authorization |
| Client | | Server |
| |<--(B)---- Access Token ---------<| |
| | | |
+---------+ +---------------+
Client向Authorization Server发出申请(/oauth/2.0/token):
grant_type = client_credentials
client_id
client_secret
scope
Authorization Server给Client返回Access Token:
access_token
token_type
expires_in
说明:此授权类型仅适用于获取与用户无关的公共信息。BTW:不返回Refresh Token。
…
流程中涉及两种Token,分别是Access Token和Refresh Token。通常,Access Token的有效期比较短,而Refresh Token的有效期比较长,如此一来,当Access Token失效的时候,就需要用Refresh Token刷新出有效的Access Token:
+--------+ +---------------+
| |--(A)------- Authorization Grant ------->| |
| | | |
| |<-(B)----------- Access Token -----------| |
| | & Refresh Token | |
| | | |
| | +----------+ | |
| |--(C)---- Access Token ---->| | | |
| | | | | |
| |<-(D)- Protected Resource --| Resource | | Authorization |
| Client | | Server | | Server |
| |--(E)---- Access Token ---->| | | |
| | | | | |
| |<-(F)- Invalid Token Error -| | | |
| | +----------+ | |
| | | |
| |--(G)----------- Refresh Token --------->| |
| | | |
| |<-(H)----------- Access Token -----------| |
+--------+ & Optional Refresh Token +---------------+
Client向Authorization Server发出申请(/oauth/2.0/token):
grant_type = refresh_token
refresh_token
client_id
client_secret
scope
Authorization Server给Client返回Access Token:
access_token
expires_in
refresh_token
scope
…
不过并不是所有人都对OAuth2.0投赞成票,有空可以看看:OAuth 2.0对Web有害吗?
此条目由老王发表在Technical分类目录,并贴了OAuth标签。将固定链接加入收藏夹。
【转】OAuth的改变的更多相关文章
- OAuth 流程与发展总结 (1.0 => 1.0a => 2.0)
OAuth 流程与发展 (1.0 => 1.0a => 2.0) 概述 概述: 开放授权协议 作用: 允许第三方应用访问服务提供方中注册的终端用户的部分资源 下面是官方描述: [OAuth ...
- OAuth2 for asp.net web api
在上篇文章中我研究了OpenId及DotNetOpenAuth的相关应用,这一篇继续研究OAuth2. https://github.com/DotNetOpenAuth http://www.cnb ...
- Lua语言中文手册 转载自网络
Programming in LuaCopyright ® 2005, Translation Team, www.luachina.net Programming in LuaProgramming ...
- ASP.NET Web API与Owin OAuth:使用Access Toke调用受保护的API
在前一篇博文中,我们使用OAuth的Client Credential Grant授权方式,在服务端通过CNBlogsAuthorizationServerProvider(Authorization ...
- iOS5.0以上使用新浪微博开放平台OAuth 续(及解决登录无效问题)
新浪微博开放平台为第三方应用提供了简便的合作模式,满足了手机用户和平板电脑用户随时随地分享信息的需求.通过调用平台的api即可实现很多微博上的功能. 本篇主要目的是记录新浪微博移动SDK iOS版本的 ...
- OAuth 2.0 授权原理
出处:http://www.cnblogs.com/neutra/archive/2012/07/26/2609300.html 最近在做第三方接入的,初步定下使用OAuth2协议,花了些时间对OAu ...
- 一步一步搭建 OAuth 认证服务器
http://www.fising.cn/2011/03/%E4%B8%80%E6%AD%A5%E4%B8%80%E6%AD%A5%E6%90%AD%E5%BB%BA-oauth-%E8%AE%A4% ...
- OAuth 2.0 / RCF6749 协议解读
OAuth是第三方应用授权的开放标准,目前版本是2.0版,以下将要介绍的内容和概念主要来源于该版本.恐篇幅太长,OAuth 的诞生背景就不在这里赘述了,可参考 RFC 6749 . 四种角色定义: R ...
- OAuth 2 开发人员指南
这是支持OAuth2.0的用户指南.对于OAuth1.0,一切都是不同的,所以看它的用户指南. 本用户指南分为两个部分,第一部分是OAuth2.0提供端(OAuth 2.0 Provider),第二部 ...
随机推荐
- sqlserver sql 循环
通过临时表进行sql循环 -----------创建临时表-------------- SELECT * INTO #tempfensitocity FROM( SELECT * FROM dbo.S ...
- git舍弃文件更改
未进行任何提交,即文件更改在工作区 # filename 对应进行操作的文件名 git checkout -- filename 已用git add 命令提交,即文件更改在暂存区 # 舍弃暂存区的修改 ...
- 使用tensorflow下的GPU加速神经网络训练过程
下载CUDA8.0,安装 下载cuDNN v5.1安装.放置环境变量等. 其他版本就不装了.不用找其他版本的关系. 使用tensorflow-gpu1.0版本. 使用keras2.0版本. 有提示的. ...
- swift - 导航设置总结加深记忆
一.创建导航 let VC=ViewController() let navigationC = UINavigationController(rootViewController: V ...
- 2018.11.05 bzoj2143: 飞飞侠(最短路)
传送门 最短路好题. 考虑对每个二维坐标建立一个高度属性. 这样每次如果在点(i,j,0)(i,j,0)(i,j,0)只能选择花费bi,jb_{i,j}bi,j跳向(i,j,ai,j)(i,j,a_ ...
- best-case analysis in real-time system
ECRTS: Exact Best-Case Response Time Analysis of Fixed Priority Scheduled Tasks motivation Real-time ...
- jmeter读取csv文件
操作步骤: 1.读取csv文件 2.编辑httpSampler
- shell脚本之正则表达式
具体参考: www.jb51.net/tools/shell_regex.html 正则表达式常用于grep AWK 等工具中
- 图片轮滚形式A
思想解读: 一共有5幅图片,初始时只显示一幅,其余四幅隐藏.然后使用trigger设置右下角的点击事件,根据点击的索引显示某幅图片.原理不算复杂. 结论: trigger的使用,模拟点击 层淡入淡出函 ...
- 【1】jQuery异步(Ajax)操作之JSONP [转]
前言: 说到AJAX就会不可避免的面临两个问题,第一个是AJAX以何种格式来交换数据?第二个是跨域的需求如何解决?这两个问题目前都有不同的解决方案,比如数据可以用自定义字符串或者用XML来描述,跨域 ...