之前写过一篇关于使用Passport.js实现Google OAuth2.0登录的流程(ref),然后自己也写了一个基于OAuth的SSO单点登录模块(ref)集成到了自己的几个项目中。最近面试几个大厂都问道了有关OAuth的问题,发现对OAuth一些原理性和安全性的问题还是理解的不是很透彻,在这里继续总结一下:
什么是OAuth
OAuth是一种第三方授权协议,委托第三方(e.g. Google,Facebook,Wechat)等对用户进行鉴权,鉴权成功后向应用发送一个信任凭证(即token)。应用可以凭借token去向第三方获取该用户授权开放的身份信息(用户名,邮箱,头像等)。
OAuth授权只是允许app从第三方处获取部分用户数据等信息,如要实现本地登录等功能,仍然需要app自行将用户信息注册到数据库,并通过session-cookie或JWT登录等方式实现
OAuth 2.0 流程
(以下仅展示授权码模式,即Authorization Code)
简化流程:
- 先向google设置redirect_uri名单,并获取appid和secret
- 用户发起登录请求,后端将请求重定向到google认证服务器,并附带client_id和redirect_uri
- 用户完成google登录,google认证服务器检查redirect_uri是否在名单内,然后重定向至此uri(应用后端)并附带code
- 应用后端收到code,使用code, appid和secret向google认证服务器换取token
- 后端使用token向google获取用户数据信息,并以此实现自己的注册和登录流程(session-cookie等)
OAuth解决了哪些安全性问题
为什么使用token而不是将google的账号+密码发送该应用,然后应用通过账号+密码从google获取用户数据
(即为什么要使用OAuth+Token的形式做第三方授权)
使用账户+密码的缺点(账户泄露、访问权限控制)
- 账户密码可能会被不法应用泄露,或者任意一个应用被破解造成泄露
- 一个应用可以拥有完整的google账户访问权限,用户无法做出相应限制
OAuth + token如何解决上述问题
如果把账号+密码当做一把永久钥匙,而token则为一把临时钥匙
- Token具有一定的时效,且可以随时由google设置为失效
- 可以通过设置scope限制每把token可以获取的用户数据范围
google为什么要验证回调uri
OAuth2.0流程第2步重定向至google服务器的过程中,client_id和redirect_uri是可以直接被明文访问到的
因此攻击者可以以此构建自己的重定向连接,并把redirect_uri替换为自己的服务器后端
这样当用户完成登录后,假设google不验证redirect_uri是否合法,将直接把code发送给攻击者的后端
(虽然攻击者即使有了code,如果没有serect仍然无法获取到token,但这相当于多了一道保护屏障)
为什么要使用code和secret换token,而不是登录后google直接返回token
google返回数据(无论是code还是token)的过程中,可能存在以下几种方式使得code或者token泄露:
- google重定向的uri不支持https,则token或code会暴露在从google到应用后端之间途径线路上的所有路由器
- 假设后端DNS被污染,token或code会直接被发送到攻击者的服务器
因此在返回凭据的过程中,使用code和secret作为两道屏障去换取token:
google先向app后端返回code而非token(code可能会因为上述两种方式造成泄露):
code只能使用一次,使用第二次时,先前换取的token会自动失效。因此就算攻击者使用code(和通过不知道什么手段获取到的secret)换到了token,当app后端收到code去第二次获取token时,整个code和token都会失效
secret只能部署给信任的服务器,服务器以此证明自己是合法的,从而换取token
总结:只返回code,再用code+secret换token,code在重定向的过程中即使泄露,没有secret也无法换取token;即使有了secret,如果使用两次code后token也会自动失效