Rails OAuth2.0 中的问题 access token 如何产生 资源服务器如何验证 access token

zhangyuxiu · 2013年11月08日 · 最后由 zw963 回复于 2015年07月23日 · 39152 次阅读

最近学习 OAuth2.0,遇到了一些问题,期望大家能帮助到我,先谢过各位了!

  1. Access Token 是如何产生的,是否有同学知道详细的算法,可以举例说明一下。

  2. 资源服务器是怎么验证 Access Token 的合法性的,是让授权服务器代为验证么?也请举例详细说明。

  3. OAuth2.0 是基于 https 协议的。在自己开发的网站中,如何搭建 https 通道呢?

只应用,搬运个,希望有帮助。 http://tools.ietf.org/html/rfc6749

听到了 OAuth 2 这个关键字,在下只好跳出来了。

  1. Access Token 产生的方法,你若要问的是 token 的文字,那并没有规定,照习惯都是用一串很长的 hash 字串。你若要问的是如何向 client 送出这个 token,那么这是 protocol 里面有定义的,只要照 protocol 跑就没问题。若你要造的是「提供 OAuth 2.0 认证的服务器」,那么直接照 RFC6749 来实作就行。
  2. Resource Server 验证合法性的方法就是去数据库里面拿出 token,看它有没有过期、有没有被撤销 (revoked) ,若「要存取的 resource 需要特定的权限,例如私讯内容」,则 token 上面要有记载相关权限,叫做 scopes。这些都没问题,就放行。最简单的 Token 用法是 Bearer Token (RFC6750) ,它只认 token 字串,不像 MAC Token 需要过一堆加密算法。
  3. OAuth 2.0 强制你要上 https,development 环境就算了,而 production 上 https 的方式就看 Rails 怎么上 https 就怎么上,也有人直接在 front-end server 上 https、后面的 load balancers 走明文的。

我要厚脸皮地来推荐一下我写的文章:

Resource Server (API) 和 Authorization Server (核发 token 的服务器) 在概念上是分离的,但实作上可以放在一起,我实作的时候就是 API 用 Grape 写,Authorization Server 用 Doorkeeper 盖,所以可以直接去存取 ActiveModel,那如果要分离在不同的 applications (例如 API 用 node.js 刻) ,那就在 API 那边想办法去拉出 token 来验证就好了,这个在 spec 里面没有定义,你可以看是要存取同一个数据库、开另一个服务器在内网拉 token,还是定期两方同步,都是方法。我比较懒,直接存取同一个数据库(其实是同一个 Model)。

希望这说明有帮到你 :)

#2 楼 @chitsaou 谢谢!哈哈,我最近这几天确实都在学习你的文章呢。写的很好!

6 楼 已删除

#2 楼 @chitsaou 针对第一点,我想知道的是 Access Token 的文字组成。比如说:access token 中应该包含有权限,客户端标识,用户标识,那么 access token 的组成就是,前几个字符串的连接的结果再 hash 么?这里提到的 hash 的意思是信息摘要的意思,还是为了之后的快速查找所采用的 key-value 的 hash 数据结构啊?

#4 楼 @chitsaou 嗯,我理解了。但是如果资源服务器和授权服务器要存储同一个数据库,那么从概念上讲说明了资源服务器和授权服务器都有 access token,可以随意存取资源么?这样是否增加了额外的风险,怎样防范授权服务器或资源服务器不会任意泄露 access token 呢。还是整个 oauth2 的前提就是,资源服务器和授权服务器都是可信的?

#2 楼 @chitsaou 我不是很懂你提到的第三点,我原来以为是 https 是需要下载安装一个安全证书就可以了。你提到了很多部分 development,production,front-end server,load balancers,这些地方都要有 https 么,为什么会分成这么多部分呢? (另外,题外的:我目前在学校,只有实验环境,看到网上说 https 证书都是付费的,请问,我可以在不付费的情况下,搞定 https 么;还有,如果要实现一个 OAuth2.0 的授权服务器,让第三方应用可以使用我们的账号来登陆,或在使用的过程中,授权用户访问我们网站的资源,需要独立域名么?)

#7 楼 @zhangyuxiu

关于 Access Token 的内容,其实是这样子:token 本身是随机产生的文字,在核发 token 的时候,用一个 JSON 包起来,里面会记载详细的信息,例如:

{
  "access_token":"XXXXXXXXXXXXXXXXXXXXXX",
  "token_type":"Bearer",
  "expires_in":3600,
  "scope": "abc def ghi",
  "refresh_token":"YYYYYYYYYYYYYYYYYYYYYYY"
}

Client 要记住的就是这些信息:

  • access_token 是去打 API 的时候要附上的参数
  • token_type 指的是这个 token 的类别,不同的类别有不同的使用方法,bearer 就是 RFC6750 定义的,其他还有 MAC,这个使用的时候似乎需要一堆加密算法,我不是很清楚。
  • expires_in 指的是几秒之后过期,Client 就必须重新向 user 要求授权,或是使用 refersh_token 来刷新
  • scope 表示权限有哪些,这个不一定会出现,若是出现,表示 user 授权的权限和 client 要求的不一致,所以必须提示 client。如果跟请求授权的时候的一样,那就不会附上,因为 client 本来就知道。
  • refresh_token 就是说过期之后可以重新刷新

资源服务器和授权服务器都有这些信息,可以判断一个 token 是否合法。

最好是不要在 token 字串附上这些信息,即使是 hash 过的也不好,因为这样子就有机会可以假造 token。要把 token 字串视为一个参照,服务器用这个 token 字串去查到真正的授权内容。

而 Client 平常在向资源服务器发出 request 的时候,实际上是只使用 access_token:

https://api.example.com/user/me?access_token=XXXXXXXXXXXXXXXXXXXXXX

至于你说授权服务器或资源服务器会滥用 token,我会说这不是 protocol 设计的目的,它就是定义授权服务器是核发授权、管理授权的机关,而资源服务器是看授权书来决定要不要让你进来的另一个机关。好比说你若要出国,你必须有护照,护照是外交部发的,而到了海关,你要把护照给海关看,海关验证无误才会放行,这里的外交部就是授权服务器,而海关就是资源服务器,它们本来就是一起合作的,不需要考虑外泄的问题。

真正需要考虑外泄,是 client 本身资料外泄,或是通信中被拦截封包。例如手机上的 app 不论如何都无法防止 token 外泄(可以越狱),所以授权服务器对这种 client(称作 public client)要当作不可信任,即使它送出 client 的帐号密码(在注册 client 的时候,授权服务器会发一组帐密),也不可以完全相信。


最后你说到 https,凭证可以自己产生,并且虽然规格书里面有规定必须验证凭证链是否无误、有没有被更上级的撤销,但你实作上就把这个验证机制关掉就行,只是依然要检查凭证是否没有改变,这个我不熟,或许可以找找看关于 SSL 凭证验证的文章。

我说的那些 front-end server 请你先忘掉 :p

独立域名应该是需要,你可以部署在 heroku 上面,也会有独立域名,例如 abc.herokuapp.com。如果 client 和 server 共用一个域名,例如 www.example.com ,那么 redirect uri 前缀一致就可能有风险,所以最好还是开在不同的域名,host 不同就行了。

#2 楼 @chitsaou 你好,请教下:为什么在 OAuth2.0 中要使用 redirect_uri,使用它有什么好处么?如果不使用的话,可以么?

redirect_uri 会在 authorization code grant flow 和 implicit flow 里面用到,这个参数指的是认证服务器在得到 user 同意或拒绝的答案的时候,要转回 client 的 uri,这个 uri 在 client 注册的时候,要预先在认证服务器写下来,可以一个,也可以多个,但若 uri 跟预先注册的不符,则认证服务器不可以转到这个 uri,以免 token 被第三方偷走。预先注册也是用来确保「认证服务器传给 client 的信息,一定是给真正的 client」。

若在授权流程中不传递 uri,可以直接使用 client 注册时所填写的那唯一一个 uri。

#2 楼 @chitsaou Authorization Code 的组成是什么呢,是随机串么?在授权服务器产生 Authorization Code 之后授权服务器记录该 code 和当前时间,当使用 Authorization code 和 redirect_uri 来获取 access token 时根据当前时间记录来判断 authorization code 是否过期,或是否与相应的 client_id 一致的么?

grant code 也是随机产生的。在 authorization code grant flow 里面,client 拿到 code 之后,必须在后台发 POST 向授权服务器换到 token,这个过程必须经过 "client authentication" ,也就是 client 本身必须登入 (login) 到授权服务器,具体方法就是放在 Authorization header 里面。

你可以看一下我写的 OAuth 2.0 Tutorial: Grape API 整合 Doorkeeper 这篇教程,在 Step 3.2 有示范这个过程。

#15 楼 @chitsaou 好的,谢谢!在你的文章中我看到说:authorization code 是短时效的,建议不超过 10 分钟,为什么呢?长失效的话有什么风险呢?还有 state 参数用于防范 CSRF 攻击,第一次是有客户端产生 state 参数向授权服务器发起 GET Authorizaion code 的请求,用户授权通过后,授权服务器重定向到客户端的 redirect_uri,并携带有 authorization code 和 state 参数,那么 state 参数的不一致是由客户端来检查的么?客户端发现 state 参数不一致就不再向授权服务器发起换取 access token 的请求了么?我的问题好多啊。。辛苦你了。。也非常非常的感谢你!

#16 楼 @zhangyuxiu 10 分钟是降低这个 code 外泄的风检,若是外泄就可能会让第三者拿到 code 并且换得 token,因为「用 code 换 token」完全无法透过 redirect_uri 确保 token 真的返回到 client 那边,攻击者若是取得 client 的 id 和 secret,完全可以伪造 token request。另一方面,code 长时效也没有实务上的用途,因为在 client 取得 code 之后,就应该马上从后台向验证服务器换得 token,那么拖太久也没有实际的用处,所以就设个 10 分钟。10 分钟我记得是 spec 里面建议的。

state 参数是 client 要检查的,不论是哪一步,只要认证服务器有返回 state,万一 state 不一致,就必须将其视为有问题,不可以继续处理。我看到 Amazon 的文件 还特别提到 state 可以用 HMAC 算法签过,返回之后再验证签名是否正确。

谢谢你提问,这样我也可以确定我对 spec 是否瞭解 :)

#2 楼 @chitsaou Hi 本人刚接触 Auth2 看了半天还是没理解,Authorization Server (AS) 这边是如何生成 access token (AT) 的,还有就是 Resource Server (RS) 这边如何去验证 请求里面的 AT 是正确的?(前提是验证的时候 RS 和 AS 没有交互~~~) 谢谢!!!

#18 楼 @fengxiangzhu Hi, Token 是随机字串,用 SecureRandom.hex 之类的东西产生就行了,重点是 client 如何取得这个 Token。另外,验证 token 的时候,Resource Server 和 Authorization Server 之间本来就没有交互,只是存取同一个数据库而已。

#19 楼 @chitsaou 好的 谢谢 我弄懂啦!!!

#2 楼 @chitsaou 请教一个问题:是关于 OAuth 服务器端的数据存储方案。看到网上说需要长期存储的数据一般使用像 Mysql 等数据库;对于像授权码等时效性很短的数据,建议利用 memcached 等缓存系统。在具体实现时,如 doorkeeper 中如何利用缓存系统?具体的缓存系统指的是什么?没有使用过,求详解。

#21 楼 @zhangyuxiu 由於 OAuth 2 裡面的 Code 和 Token 都有「過期」這個機制,若 client 使用過期的 code 要視為攻擊事件,有幾個相對應的處置方式(記得是要把該 code 生成的 token 給撤銷?),而過期的 token 是在 resource server 檢查的,「過期」跟「找不到」是兩回事,要返回不同的 error。

為了要知道該現存的 code 或 token 是否過期,必須保存原本的值,所以數據不會刪掉,而是由程序來判斷是否過期。

至於 doorkeeper,裡面似乎沒有利用到緩存系統…

23 楼 已删除

问问 使用 Bearer Token 是不是通过随便生成的一个串如(1111111),然后保存到认证服务器的数据库里。 在发给 client 端。client 端按固定格式 Authorization: Bearer 1111111111 发送给认证服务器。 然后认证服务器从数据库里查找是不是一样。就可以认为是认证通过了?

随便问下第一步 客户端需要申请 APPID 和 APPKEY。 请问这个 APPID 和 APPKEY 怎么生成,还是说通过 HASH 算法生成一个串即可?

#22 楼 @chitsaou 问问 使用 Bearer Token 是不是通过随便生成的一个串如(1111111),然后保存到认证服务器的数据库里。 在发给 client 端。client 端按固定格式 Authorization: Bearer 1111111111 发送给认证服务器。 然后认证服务器从数据库里查找是不是一样。就可以认为是认证通过了?

随便问下第一步 客户端需要申请 APPID 和 APPKEY。 请问这个 APPID 和 APPKEY 怎么生成,还是说通过 HASH 算法生成一个串即可?

看过 @chitsaou 写的有关 OAuth2 的好多文章,虽然仍旧没完全弄懂,但是对我帮助好大,

这篇帖子的大量回复超级有用,这样的帖子,竟然没有标记精华,无语了。

需要 登录 后方可回复, 如果你还没有账号请 注册新账号