之前写过一篇关于使用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)

简化流程:

  1. 先向google设置redirect_uri名单,并获取appid和secret
  2. 用户发起登录请求,后端将请求重定向到google认证服务器,并附带client_id和redirect_uri
  3. 用户完成google登录,google认证服务器检查redirect_uri是否在名单内,然后重定向至此uri(应用后端)并附带code
  4. 应用后端收到code,使用code, appid和secret向google认证服务器换取token
  5. 后端使用token向google获取用户数据信息,并以此实现自己的注册和登录流程(session-cookie等)

OAuth解决了哪些安全性问题

  1. 为什么使用token而不是将google的账号+密码发送该应用,然后应用通过账号+密码从google获取用户数据

    (即为什么要使用OAuth+Token的形式做第三方授权)

    1. 使用账户+密码的缺点(账户泄露、访问权限控制)

      • 账户密码可能会被不法应用泄露,或者任意一个应用被破解造成泄露
      • 一个应用可以拥有完整的google账户访问权限,用户无法做出相应限制
    2. OAuth + token如何解决上述问题

      如果把账号+密码当做一把永久钥匙,而token则为一把临时钥匙

      • Token具有一定的时效,且可以随时由google设置为失效
      • 可以通过设置scope限制每把token可以获取的用户数据范围
  2. google为什么要验证回调uri

    OAuth2.0流程第2步重定向至google服务器的过程中,client_id和redirect_uri是可以直接被明文访问到的

    因此攻击者可以以此构建自己的重定向连接,并把redirect_uri替换为自己的服务器后端

    这样当用户完成登录后,假设google不验证redirect_uri是否合法,将直接把code发送给攻击者的后端

    (虽然攻击者即使有了code,如果没有serect仍然无法获取到token,但这相当于多了一道保护屏障)

  3. 为什么要使用code和secret换token,而不是登录后google直接返回token

    google返回数据(无论是code还是token)的过程中,可能存在以下几种方式使得code或者token泄露:

    1. google重定向的uri不支持https,则token或code会暴露在从google到应用后端之间途径线路上的所有路由器
    2. 假设后端DNS被污染,token或code会直接被发送到攻击者的服务器

    因此在返回凭据的过程中,使用code和secret作为两道屏障去换取token:

    1. google先向app后端返回code而非token(code可能会因为上述两种方式造成泄露):

      code只能使用一次,使用第二次时,先前换取的token会自动失效。因此就算攻击者使用code(和通过不知道什么手段获取到的secret)换到了token,当app后端收到code去第二次获取token时,整个code和token都会失效

    2. secret只能部署给信任的服务器,服务器以此证明自己是合法的,从而换取token

    总结:只返回code,再用code+secret换token,code在重定向的过程中即使泄露,没有secret也无法换取token;即使有了secret,如果使用两次code后token也会自动失效