OAuth2.0是目前最主流的授权框架,几乎所有的第三方登录、开放平台授权都是用的OAuth2.0,比如微信登录、微博登录、GitHub登录等等。但是很多人虽然天天在用OAuth2.0,但是对它的原理和底层机制了解不多,只知道怎么调用,不知道为什么这么设计,遇到问题也不知道怎么排查。今天就来深入剖析一下OAuth2.0的认证原理,从授权流程、角色定义、各种授权模式、令牌机制、安全设计等几个方面,带大家深入理解OAuth2.0的底层机制。
一、OAuth2.0是什么,解决了什么问题
在讲原理之前,先搞清楚OAuth2.0是什么,它解决了什么问题。
OAuth(Open Authorization,开放授权)是一个开放的授权标准,允许用户授权第三方应用访问他们存储在服务提供者上的资源,而不需要把用户名和密码提供给第三方应用。OAuth2.0是OAuth协议的2.0版本,是目前的主流版本,比1.0更简单、更安全、更灵活。
举个例子,你想用微信登录一个第三方网站,这时候你不需要把微信的用户名和密码告诉这个网站,只需要在微信的授权页面确认授权,网站就能获取到你的微信基本信息(昵称、头像等),完成登录。这个过程就是用的OAuth2.0,它让用户在不泄露密码的情况下,授权第三方应用访问自己的资源,既方便又安全。
在OAuth2.0出现之前,如果第三方应用想访问用户在其他平台的资源,只能让用户把用户名和密码告诉第三方应用,第三方应用用用户的密码去登录获取资源。这样做非常不安全,第一,用户的密码泄露给了第三方应用,第三方应用可以随便访问用户的所有资源,甚至可以改密码;第二,如果用户改了密码,所有授权的第三方应用都失效了;第三,用户无法限制第三方应用的访问权限和有效期,要么不给,要么给全部。
OAuth2.0就是为了解决这些问题而设计的,它引入了授权层,把用户认证和第三方应用授权分开,第三方应用不需要用户的密码,只需要获取一个授权令牌(access token),就可以访问用户的资源,而且令牌有权限范围和有效期,用户可以随时撤销授权,既方便又安全。
二、OAuth2.0的四个角色
OAuth2.0定义了四个角色,理解了这四个角色,就理解了OAuth2.0的基本结构。
1. Resource Owner(资源所有者):就是用户,资源的拥有者,比如微信用户,用户拥有自己的微信头像、昵称、好友等资源。资源所有者可以授权第三方应用访问自己的资源。
2. Client(客户端):就是第三方应用,比如那个想用微信登录的网站,它想访问用户在微信上的资源,需要获得用户的授权。
3. Authorization Server(授权服务器):就是服务提供者的授权服务器,比如微信的授权服务器,负责验证用户身份,处理用户授权,颁发令牌给客户端。
4. Resource Server(资源服务器):就是服务提供者的资源服务器,比如微信的用户信息服务器,存储用户的资源,负责验证客户端的令牌,如果令牌有效,就返回对应的资源。
在实际实现中,授权服务器和资源服务器可以是同一个服务器,也可以是分开的,比如微信的授权服务器和用户信息服务器可能是分开的,但是对客户端来说,不需要关心这个,只需要知道授权地址和资源地址就行。
这四个角色之间的交互,就是OAuth2.0的基本流程:客户端想访问资源所有者的资源,先去授权服务器获得用户的授权,拿到访问令牌,然后用令牌去资源服务器获取资源。整个过程,用户的密码只在授权服务器输入,不会泄露给客户端。
三、OAuth2.0的四种授权模式
OAuth2.0定义了四种授权模式(Authorization Grant),适用于不同的场景,这是OAuth2.0的核心内容,也是很多人容易混淆的地方。
1. Authorization Code(授权码模式)
这是最安全、最常用的模式,适用于有后端的Web应用。流程是这样的:
- 客户端把用户引导到授权服务器的授权页面,用户登录并确认授权。
- 授权服务器把用户重定向回客户端的回调地址,同时带上一个授权码(code)。
- 客户端用授权码,加上自己的clientid和clientsecret,向授权服务器请求访问令牌(access token)。
- 授权服务器验证授权码和客户端身份,验证通过后颁发访问令牌,还可能同时颁发刷新令牌(refresh token)。
- 客户端用访问令牌去资源服务器获取用户资源。
这个模式的特点是,授权码通过浏览器传递,但是访问令牌是在客户端后端和授权服务器之间传递的,不会经过浏览器,也不会暴露给用户,安全性很高。而且客户端的client_secret是存在后端的,不会泄露到前端。所以有后端的Web应用,都应该用这种模式。
2. Implicit(简化模式/隐式模式)
这种模式适用于没有后端的纯前端应用,比如单页应用(SPA)、浏览器插件。流程和授权码模式类似,但是没有授权码这一步,授权服务器直接把访问令牌通过URL的hash片段返回给客户端,客户端直接从URL里取出令牌。
这种模式的特点是,流程简单,少了一步用授权码换令牌的过程,但是访问令牌会暴露在浏览器的URL里,安全性比授权码模式低,而且令牌的有效期一般比较短,也没有刷新令牌。所以这种模式只适用于安全性要求不高的纯前端应用,而且令牌有效期要短。
3. Resource Owner Password Credentials(密码模式)
这种模式适用于用户高度信任客户端的情况,比如客户端是官方的应用,或者是用户自己开发的应用。流程是这样的:
- 用户把自己的用户名和密码直接告诉客户端。
- 客户端用用户的用户名和密码,加上自己的clientid和clientsecret,向授权服务器请求访问令牌。
- 授权服务器验证用户名密码和客户端身份,验证通过后颁发访问令牌。
这种模式的特点是,用户需要把密码告诉客户端,所以安全性最低,只有在用户非常信任客户端的情况下才能用,比如官方的手机APP。而且这种模式下,客户端不能存储用户的密码,用完就应该丢弃,而且令牌有效期要短,最好有刷新令牌。
4. Client Credentials(客户端模式)
这种模式适用于客户端访问自己的资源,而不是访问用户的资源,比如应用之间的接口调用,或者客户端访问公共资源。流程是这样的:
- 客户端用自己的clientid和clientsecret,向授权服务器请求访问令牌。
- 授权服务器验证客户端身份,验证通过后颁发访问令牌。
这种模式没有用户参与,客户端代表自己访问资源,令牌代表的是客户端的身份,而不是用户的身份。适用于机器对机器(M2M)的接口调用,或者访问不需要用户授权的公共资源。
这四种模式,各有各的适用场景,实际使用的时候,要根据应用的类型和安全要求选择合适的模式。大部分情况下,有后端的Web应用用授权码模式,纯前端应用用简化模式,官方APP可以用密码模式,应用之间调用用客户端模式。
四、令牌机制:Access Token和Refresh Token
OAuth2.0的核心是令牌(Token),理解了令牌机制,就理解了OAuth2.0的安全设计。
Access Token(访问令牌):这是客户端访问资源服务器的凭证,客户端拿到访问令牌之后,就可以在有效期内用这个令牌访问用户的资源,不需要再经过用户授权。访问令牌是有时效性的,过期之后就失效了,需要重新获取。访问令牌的有效期一般比较短,比如几个小时,这样即使令牌泄露了,危害也有限,因为很快就过期了。
访问令牌的格式没有在OAuth2.0规范里规定,可以是任意字符串,实际实现中,常见的有随机字符串、JWT(JSON Web Token)等。随机字符串的令牌,需要资源服务器去授权服务器校验令牌的有效性;JWT格式的令牌,本身包含了用户信息、权限、过期时间等,用签名保证不被篡改,资源服务器可以自己校验,不需要去授权服务器查询,性能更好,但是令牌一旦签发,在有效期内就无法撤销,除非做黑名单。
Refresh Token(刷新令牌):这是用来刷新访问令牌的令牌,当访问令牌过期之后,客户端可以用刷新令牌向授权服务器申请一个新的访问令牌,不需要用户重新授权。刷新令牌的有效期一般比较长,比如几天或者几个月,而且刷新令牌只能用一次,用了之后就失效了,授权服务器会颁发一个新的刷新令牌。
为什么要有刷新令牌呢?因为访问令牌有效期短,过期了如果要用户重新授权,体验很差,特别是手机APP,总不能让用户每隔几个小时就重新登录一次。有了刷新令牌,客户端就可以在后台自动刷新访问令牌,用户无感知,体验很好。而且刷新令牌只在客户端后端和授权服务器之间传递,不会暴露给浏览器,安全性也有保障。
刷新令牌不是必须的,简化模式就没有刷新令牌,因为纯前端应用存储刷新令牌不安全。授权码模式和密码模式一般都有刷新令牌,客户端模式一般没有,因为客户端模式的令牌代表的是客户端身份,不需要频繁刷新。
令牌的存储也很重要,访问令牌和刷新令牌都要安全存储,不能泄露。Web应用一般存在后端的session或者数据库里,不要存在前端的localStorage或者cookie里,防止XSS攻击窃取。手机APP存在安全存储区里,防止被反编译窃取。
五、OAuth2.0的安全设计和常见问题
OAuth2.0的设计考虑了很多安全问题,但是如果使用不当,还是会有安全风险。这里讲几个常见的安全问题和注意事项。
1. 回调地址校验:授权服务器在重定向回客户端的时候,一定要校验回调地址,必须和客户端注册的回调地址一致,或者在白名单里,不能随便回调到任意地址。不然攻击者可以构造恶意的回调地址,把授权码或者令牌窃取走。这是OAuth2.0最常见的安全漏洞之一,很多平台都出过这个问题。
2. state参数防CSRF:在授权请求里,应该带上一个随机的state参数,授权服务器回调的时候会原样返回,客户端要校验state是否和之前发的一致,防止CSRF攻击。如果没有state校验,攻击者可以构造一个授权请求,诱导用户点击,用户授权之后,授权码会回调到攻击者控制的地址,攻击者就可以用这个授权码获取令牌,冒充用户。所以state参数是必须的,不能省略。
3. 授权码一次性使用:授权码只能用一次,用了之后就失效了,不能重复使用。不然如果授权码泄露了,攻击者可以重复用授权码获取令牌。而且授权码的有效期要短,比如几分钟,过期就失效,减少泄露的风险。
4. HTTPS传输:整个OAuth2.0的流程,都必须用HTTPS传输,包括授权页面、回调地址、令牌请求接口、资源接口,都要用HTTPS,防止中间人攻击窃取令牌或者授权码。HTTP传输的OAuth2.0是完全不安全的,绝对不能用。
5. 令牌的权限范围(scope):授权的时候,要明确指定权限范围(scope),客户端只能访问授权范围内的资源,不能访问超出范围的资源。而且授权页面要明确告诉用户,客户端申请了哪些权限,让用户知情,用户可以选择同意或者拒绝。不要给客户端超出必要的权限,遵循最小权限原则。
6. 令牌撤销机制:用户应该能够随时查看和撤销已经授权的客户端,比如微信的授权管理页面,用户可以看到哪些应用授权了,随时可以取消授权。取消授权之后,对应的访问令牌和刷新令牌都应该失效,客户端不能再访问用户资源。
这些安全问题,不管是做OAuth2.0的服务端,还是做客户端,都要注意,很多安全漏洞都是因为这些细节没做好导致的。OAuth2.0本身的设计是安全的,但是用不好还是会出问题。
六、写在最后
OAuth2.0认证原理剖析:深入理解底层机制。
以上就是对OAuth2.0认证原理的深入剖析,从它解决的问题、四个角色、四种授权模式、令牌机制,到安全设计和常见问题,都做了详细的介绍。OAuth2.0看起来简单,就是几个接口调用,但是深入理解之后,会发现它的设计非常巧妙,考虑了很多安全和场景的问题,是一个非常优秀的授权框架。
现在OAuth2.0已经成为了行业标准,几乎所有的开放平台都在用,不管是做后端开发还是前端开发,都应该了解和掌握OAuth2.0的原理和使用。而且在OAuth2.0的基础上,又发展出了OpenID Connect(OIDC),在OAuth2.0的基础上增加了身份认证的功能,现在也越来越流行,有兴趣的同学可以继续深入学习。
技术是学无止境的,很多我们天天在用的东西,深入了解之后,会发现里面有很多学问,很多设计的巧思。希望这篇文章能帮助大家更深入地理解OAuth2.0,知其然更知其所以然,在实际工作中用好OAuth2.0,避免踩坑。
最后用一句话结尾:"简单的接口背后,往往有不简单的设计。"愿我们都能深入理解技术的原理,而不只是停留在会用的层面。
评论(0)
暂无评论,快来抢沙发~
评论功能仅对会员开放,请先登录
登录