V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
nianyu
V2EX  ›  信息安全

关于 token 过期的疑惑,为什么需要 refresh token?

  •  2
     
  •   nianyu · 2021-12-08 18:00:08 +08:00 · 12270 次点击
    这是一个创建于 1110 天前的主题,其中的信息可能已经有所发展或是发生改变。

    平时做项目早就习惯了,在拦截器中将 access token 放入 request header 头中,如果 token 过期,则请求 refresh token 接口,拿到一个新 token ( babalabala ,还有很多细节 isRereshing and 刷新过程中将新请求的 request 放入队列...)

    但是一直不是很明白为什么需要 refresh token

    疑问 1: 为什么不能将 token 设置的有效期长一些,失效就直接重新登陆,而需要不停的刷新 token, 毕竟 refresh token 过期的时候,总还是要重新登陆的。

    在网上搜了相关问答,有人说用户不能正在进行操作时,突然因为 token 过期重新登陆了了,然而当 refresh 也过期的时候,依然要重新登陆。

    疑问 2: 有人说是为了安全问题,如果 token 被人劫持,就可以冒充用户进行一些操作,所以需要将 token 的有效期设置的非常短,这样即使破译了 token ,没过几分钟就失效了,我觉得没有道理啊,客户端通常将 access token 喝 refresh token 存在 storage 中,或者说既然能拿到 token 为什么拿不到 access token 。

    42 条回复    2022-12-30 09:44:58 +08:00
    markgor
        1
    markgor  
       2021-12-08 18:20:47 +08:00   ❤️ 3
    兼容场景比较大的时候,就能体现出 refreshToken 的意义;
    比方第三方服务商,简称 A ;
    自己的后台,简称 B ;
    自己的前端,简称 C ;

    疑问 1:
    参考 JWT ,token 中可能包含着用户信息,token 有效期过了但 refreshToken 的没过,此时只需要通过 refreshToken 就能更新到用户信息了,对用户无感知。
    如果没有 refreshToken ,则需要用户重新登录进行验证,这个时候会产生交互操作。

    >然而当 refresh 也过期的时候,依然要重新登陆。
    正常情况下通过 refreshToken 刷新,refreshToken 会返回一个新的,过期时间上自然是会延迟了,所以不会存在操作中需要重新登录。

    *如果是说把用户 账号密码存起来,过期后再静默进行登录达到刷新 token 的话,那其实账号密码就充当了 refreshToken 角色,但这种做法可能会导致泄露用户账号密码信息,所以需要 refreshToken 。


    疑问 2:
    当 C 需要调用 A 的服务,会携带 token 发去 B ,由 B 发去 A ;
    此时如果 C 发送的 token 超时,B 会通过 refreshToken 去刷新 token 的时长,然后重发请求给 A ,再返回结果给 C ;
    这种场景中 refreshToken 并不传给 C ,而是 B 自己保存着。


    以上是我自己对于 token 和 refreshToken 的见解,不过如果 token 是我自己产生的话,我会采取单一 token 形式进行,因为我自身业务可以不用考虑上面的场景,但 oauth 的设计考虑到的场景比较多,所以才会这样。
    golangLover
        2
    golangLover  
       2021-12-08 18:25:55 +08:00 via Android
    refresh token 是本地储存的,发出去的是 accesstoken. 所以 access token 容易被通讯拦截。
    ikas
        3
    ikas  
       2021-12-08 18:29:49 +08:00   ❤️ 1
    对的..就是不安全...
    假设只有客户端与 server
    refreshToken 只是减少了传输时被偷的可能,因为大部分时间走 access token..当然因为 refreshToken 交换的时机少,你可以加入一些其他的判断..但是真的你的传输通道被监听...也没多大用
    heeeeeem
        4
    heeeeeem  
       2021-12-08 18:37:29 +08:00   ❤️ 1
    https://www.v2ex.com/t/462507
    这有个相关的贴
    除去复杂的业务场景
    一般就是怕只有一个 access token 在传来传去的 传输 过程中不安全,
    但要是客户端被黑,那搞几个 token 都没用
    我觉得 在传输过程中的安全可以由 https 保证,一般业务用一个 token 也无妨
    oott123
        5
    oott123  
       2021-12-08 20:04:14 +08:00   ❤️ 6
    accessToken 可以是 stateless 的,比如 JWT ,不做吊销,用的时候直接空手验证,不需要访问数据库
    refreshToken 存到数据库里,做吊销
    nianyu
        6
    nianyu  
    OP
       2021-12-08 20:43:16 +08:00
    @markgor @ikas 感谢
    @heeeeeem 好的,我去看看
    Rocketer
        7
    Rocketer  
       2021-12-08 22:36:43 +08:00 via iPhone
    5 楼正解。简单来说就是 access token 负责效率,不查验状态直接用。而 refresh token 负责安全,每次使用都要查数据库,从而实现服务端注销等操作。
    xtinput
        8
    xtinput  
       2021-12-08 22:55:09 +08:00   ❤️ 1
    我上家公司后台就是 refreshToken+accessToken ,refreshToken 用的时候是放 body 里面,rsa 加蜜传给后台的,accessToken 是放 header 里不加密传递(效率),然后 accessToken 有效期 2 小时,refreshToken 是 7 天。rsa 公钥是登录的时候后台交给客户端的,这个公钥是一个 rsa 密钥库里面随机拿的(有好几千个),客户端只将 refreshToken 存在应用配置信息里,accessToken 是内存缓存。部分接口 body 数据 rsa 加密,header 里包含了 rsa 公钥的编号,这个编号不是内部人士还真不知道,这样就确保安全系数不低了,再加上 https
    Rocketer
        9
    Rocketer  
       2021-12-09 02:32:36 +08:00   ❤️ 19
    @xtinput 其实没必要那么复杂,refresh token 更像传统意义上的 session id ,能拿到这个信息的人,基本用不到这个信息就可以直接干坏事了。

    现代架构之所以用 token ,是因为后端普遍采用分布式,各服务器之间同步状态(比如 session )的开销很大,所以干脆不用状态,而是给个 token ,后端各自验证 token 的有效性而无需与其他服务器沟通,这就是所谓的 stateless 。

    stateless 在绝大多数时候都没问题,但我们却不太可能实现彻底的无状态。比如用户修改了密码,服务端想强制他重新登录,这就得通知各个服务器不要再接受之前的 token 了。

    要解决这个问题,常见的有 3 种方法:
    1 、找个地方(内存、数据库等)记录合法的 token ,每次验证 token 都查一下这个 token 是否还在。
    2 、找个地方(内存、数据库等)记录 token 的黑名单,每次验证 token 都查一下这个 token 是否在黑名单里。
    3 、让 token 自己失效。

    前两种都是有状态的方法,仍然避免不了状态同步的瓶颈,所以我们一般采用第三种方法。

    那怎么让 token 自己失效呢? token 里一般都有时间信息,所以只需把有效期设得短一点,不再更新它,它就过期失效了。

    这就引出了更新的问题,怎么更新 token 呢?我们一般用另一个 token ,这就是所谓的 refresh token 。服务端收到 refresh token 以后,是要检查黑名单或者白名单的,所以更新 token 这一步是有状态的。只有 refresh token 有效,才会下发 access token ,这样就把对状态同步的需求限制到了一个很小的范围内,从而降低状态同步成本。

    自此,有状态和无状态实现了有机结合,在一起过上了幸福快乐的日子。
    VeryZero
        10
    VeryZero  
       2021-12-09 09:10:02 +08:00
    @Rocketer 那 refresh token 什么时候校验呢?是每次请求都校验还是 access token 过期了再校验?

    如果是前者的话,相对于把 access token 放缓存里有什么区别?如果是后者,需要等几个小时才注销怕是不合适吧。。

    如果只是为了做注销,完全可以把 access token 放缓存里,如果担心性能可以降低读取缓存的频率。比如每 10 分钟校验一次,其他时候都是无状态模式
    VeryZero
        11
    VeryZero  
       2021-12-09 09:11:18 +08:00
    我其实也一直疑惑 refresh token 的意义,楼上大佬们的解释感觉不太让人信服,期待更多信息
    VeryZero
        12
    VeryZero  
       2021-12-09 09:21:38 +08:00
    另外我默认咱们讨论是是自己实现的用户登录功能的 refresh token ,而不是 OAuth2 中的。后者 refresh token 确实有存在的必要性。
    MX123
        13
    MX123  
       2021-12-09 09:29:01 +08:00
    @Rocketer 你这意思 refresh token 主要是解决状态同步问题,而不是安全问题是吗?
    vicalloy
        14
    vicalloy  
       2021-12-09 09:39:56 +08:00   ❤️ 1
    @VeryZero refresh token 要到数据库校验的。
    1. 在到期前,token 无法失效。如果你的 token 是永久的,导致这个用户及时修改密码,token 被盗,这个 token 依旧有效。
    2. 如果 token 有效期比较短,比如 1 天。被盗的这个 token 在有效期内可以被正常使用。
    3. 等到被盗的 token 失效后,“小偷”无法继续使用你的 token 做坏事。
    wanguorui123
        15
    wanguorui123  
       2021-12-09 09:40:14 +08:00
    其实有 Redis 后 refreshToken 没什么用
    timethinker
        16
    timethinker  
       2021-12-09 09:54:59 +08:00   ❤️ 4
    如果你的 AccessToken 不是 Stateless 的(意味着每次都要读取状态,校验 AccessToken 的合法性,判断这个 AccessToken 是否已经被撤销,或者是否已经被替换),那么 RefreshToken 就没有太大的意义。

    如果你的 AccessToken 不需要读取状态(无论是数据库或者缓存),仅凭 Token 本身的签名信息就能确定它的合法性(如 JWT ),那么 RefreshToken 的存在就相当于有了一个检查点,可以在检查点确认是否还可以续签。

    因此 AccessToken 的有效期应当尽量设置短一点,通过 AccessToken 访问,只要通过签名校验合法即可通行,无序读取额外的状态来进一步确认是否撤销,当 AccessToken 过期以后再通过 RefreshToken 读取额外的状态(数据库 /缓存)确认是否继续签发。
    cenbiq
        17
    cenbiq  
       2021-12-09 09:58:46 +08:00
    wanguorui123
        18
    wanguorui123  
       2021-12-09 10:01:02 +08:00
    @cenbiq accessToken 充当 SessionID
    cenbiq
        19
    cenbiq  
       2021-12-09 10:03:55 +08:00   ❤️ 1
    @VeryZero 流程是这样的:1.登录成功获得 refresh token 并持久化 -> 2.通过 refresh token 请求刷新得到 access token 并临时储存 -> 3.请求业务接口使用 access token -> 4. access token 过期或者快过期再次回到「 2 」 -> 5.refresh token 也过期则生命周期结束,需重新登录。
    VeryZero
        20
    VeryZero  
       2021-12-09 10:13:29 +08:00
    如果我没理解错,refresh token 主要用途是降低 access token 被盗后的损失。
    之前我还以为每次请求都要带上 access token 和 refresh token ,那这样就没有意义了。如果只携带 access token 就能说通了。
    其他功能,类似于注销踢人、自动续期等等都是附加特性,只靠 access token 也可以实现。
    但是如果只依赖 access token 实现自动续期,一旦 access token 被盗,服务端是无法感知的,还是会一直帮「盗号者」续期 token 。refresh token 可以一定程度上缓解这个问题
    Qsong
        21
    Qsong  
       2021-12-09 10:14:11 +08:00   ❤️ 1
    token 的过期时间一般会比 refeshToken 的过期时间短很多,保证 token 被盗取后无法持久的做坏事。
    refeshToken 一般会存储在后端服务器,而不会放在客户端,如果说放在客户端,那 token 将毫无意义
    当 token 过期后,后端会利用 refeshToken 返回一个新的 token 给到前端使用,而不是前端拿一个过期的 token 获取一个新的 token ,只要 token 过期了,那么 token 就已经是无效的了,只能用新的 token 才可以正常使用。
    一般后端会将 token 放在请求头里返回给前端,前端只要拿着 header 里的 token 调用接口就行了,对于是否刷新了 token 也是无感的
    dingwen07
        22
    dingwen07  
       2021-12-09 10:15:39 +08:00 via iPhone
    如果用户持续地访问这个网站,他们可以一直保持登录状态,而不需要定期重新登录。
    robinlovemaggie
        23
    robinlovemaggie  
       2021-12-09 10:15:54 +08:00
    token 的设计本身不负责安全性,所以劫持问题不应在 token 这里讨论。
    aboat365
        24
    aboat365  
       2021-12-09 10:16:03 +08:00   ❤️ 1
    token 翻译成中文就是令牌。因为 HTTP1.1 协议每次请求都是独立的,不能复用的连接。所以,在每次请求中都需要带上令牌。令牌有效期越长,那么请求携带次数就越多,传输过程越容易暴露令牌,导致安全性下降。总而言之,就是一个令牌使用时间和次数越多,那么在使用过程中越不安全。反之,把令牌有效期设置的越短,那么就越安全。但如果这样子,将导致用户体验很差。那么如何解决这个矛盾的?对于经典的 web 应用,令牌的有效时间是 30 分钟,在有效期内使用令牌请求,后端将刷新令牌的有效期。如果超过 30 分钟再发起请求,服务端会要求前端带上 refresh token ,即刷新令牌。刷新令牌有效期可以设置很多天,比如设置一周。刷新令牌设置这么长时间那不是很不安全?其实不是的,刷新令牌相比前面每次请求使用的令牌来说,区别在于低频次使用。虽然有效期长得多,但使用次数非常低,有的方案只用一次。如前所述,使用刷新令牌换取普通令牌,并获取一个新的刷新令牌,原刷新令牌作废。如此,便实现了用户登录后,只要不超过一周内访问服务,那便可以一直免登录(例如你手机上的微信)。最后关于令牌本身,可以是服务端随机生成的 session id ,也可以是服务端不保存映射关系的 JWT ,主要看具体应用场景。

    最后归纳一下,token 和 refresh token 的区别在于有效期一个短一个长。使用上 token 用于每次请求,refresh token 用于 token 过期后去换取新的 token 和 refresh token 。这样设计的目的,就是为了解决安全性和使用体验之间的矛盾。
    libook
        25
    libook  
       2021-12-09 10:29:41 +08:00   ❤️ 1
    从需求出发:
    1. 用户不希望做每个操作都进行登录验证操作,所以需要让用户会话有一定的有效时间;
    2. 用户不希望在长期不做操作的情况下,比如离开电脑前,甚至电脑交给他人使用的时候,会话仍然有效,以至于可以被他人操作,所以用户会话的有效时间不能太长;
    3. 用户不希望自己正在使用的时候会话突然过期,所以需要在用户会话过期之前,如果用户还有操作的话,就对会话续期(刷新)。

    题主疑问 2 里面说的“有人说”的观点是错误的,安全性都是相对的,没有网络通信安全和客户端安全作为保障,token 机制不管怎么用都是无法保证安全的。
    onhao
        26
    onhao  
       2021-12-09 10:40:19 +08:00
    我只说我们的 refresh token ,这个接口就是起到延期 token 的作用,可以让用户能在一定的日期内登录始终是有效的,这样避免了 token 一直固定也可提升用户体验-无感登录
    Rocketer
        27
    Rocketer  
       2021-12-09 11:11:16 +08:00 via iPhone
    @VeryZero refresh token 每次使用都要校验状态。

    至于什么时候拿 refresh token 去换取一个新的 access token ,那是客户端的事。它可以在 access token 即将到期时主动更新,也可以在服务端返回失效错误后再去更新,还可以在任何自己认为需要的时候去更新(比如自己刚修改了密码,于是立刻去获取个新的 access token )
    neetrorschach
        28
    neetrorschach  
       2021-12-09 14:05:58 +08:00   ❤️ 1
    说下我在某外企客户那儿对接他们的 oauth2 的开发指导文档吧。
    用户每次登录会获取 access token 和 refresh token
    前端保存 access token 和 refresh token
    每次前端请求带上 access token ,后端用 access token 去 oauth2 服务验证是否有效。
    如果无效则反馈给前端,前端会再次用 refresh token 发送请求。oauth2 会验证 refresh token 是否在有效期内。如果有效,则返回新的 access token 和 refresh token (延长有效期)。前端更新存储的 token 。
    如果 refresh token 无效则跳转到登录页。
    refresh token 设置了一周时间。如果用户每天用,其实基本不会要求重登录。只有长期不用才会要求重新登录。
    dinding
        29
    dinding  
       2021-12-09 14:43:01 +08:00
    1 从权限上说,refresh token 是和授权服务器之间的凭证,access token 是和资源服务器之间的凭证。
    2 从实现上,有效的 access token 可以有多个,但是 refresh token 只能有一个。所以前者可以实现为 self-contanined(包含了请求资源的所有信息),无需持久化存储;但后者必须存在某个地方。
    3 从安全性上,为了防止泄漏,access token 一般不会有很长的有效期。这里,“泄漏”的方式,除了楼主说的 storage 外,还包括比如日志,非 https 的 cookie ,不可信的第三方等等。显然,经常使用的 access token, 要比不经常使用的 refresh token 泄漏的概率高。此外,refresh token 是要配合 client id/secret 一起使用的,通常 client id/secret 不会和 refresh token 存在一个地方。
    whileFalse
        30
    whileFalse  
       2021-12-09 16:26:02 +08:00 via iPhone
    为了把你踢下线。(服务器主动使登陆状态失效)
    session 机制只要删除服务器 redis key 即可把你踢掉
    token 机制中 access token 是离线验证的不走 redis ,所以用一个较短的寿命,等他过期了你拿着 refresh token 去续期的时候服务器不给你续,就是踢下线了。
    AsherTan
        31
    AsherTan  
       2021-12-09 16:45:54 +08:00
    1. AccessToken 和 RefreshToken 一般都是配合着使用,多用于第三方 openAPI 的鉴权,自己后端服务的登录鉴权的 token 一般不会弄个 RefreshToken 的。
    2. AccessToken 和 RefreshToken 基于安全性考虑一般都是建议保存在服务端的,是由服务端去使用,不会在客户端直接使用 Access Token 的。
    3. AccessToken 还是基于安全考虑,有效时间比较短,一般两小时。可以使用 RefreshToken 定时请求接口获取新的 AccessToken 。
    Akesudia
        32
    Akesudia  
       2021-12-09 17:09:18 +08:00
    1.Refresh Token 确实提高了安全性,短有效期就是防止 Access Token 被窃取后攻击者长期保持有效会话,如果使用了 Token 黑名单机制就无所谓了(但破坏了 stateless)
    2.Access Token 在每个请求中附带,Refresh Token 只在 Access Token 失效时传输,被中间人窃取机会比 Access Token 小。
    unco020511
        33
    unco020511  
       2021-12-09 17:25:50 +08:00
    @VeryZero #20 我还真没见过每次都携带 refresh token 的...
    SheHuannn
        34
    SheHuannn  
       2021-12-09 17:38:17 +08:00
    大致看了上边的回复,access token + refresh token 的模式更多的是解决了 access token 续签的问题,对改进用户体验有帮助,好像在保证数据安全性方面没起到啥实质性作用。
    ipwx
        35
    ipwx  
       2021-12-09 17:41:40 +08:00
    加密签名,可以验证 AccessToken 没有被篡改和捏造(解密出来“什么时候过期”,它就一定是正确的过期时间。客户端无法捏造这个时间)。那么每个后端服务器只要有一套解密的密钥就行了。

    甚至集中的认证服务器,可以根据要访问的业务,调用不同的加密密钥去加密,这样的 Access Token 自然只有对应的业务才会承认。

    这就是 Stateless 的核心 —— AccessToken 不用连接任何中央服务器,就能迅速检验它是合法的、不是捏造的。时间戳附带签名就能证明其真实性。
    chi1st
        36
    chi1st  
       2021-12-09 17:43:44 +08:00
    求问 RefreshToken 到底应该放在哪里储存呢?
    mrliusg
        37
    mrliusg  
       2021-12-09 19:57:48 +08:00
    RFC6819 里详细解释了 OAuth 2.0 面临的威胁和应该做的安全设计。从整体上看就可以理解为什么要设计两个 Token ,且具有不同的特性。
    dany813
        38
    dany813  
       2021-12-10 11:45:41 +08:00
    RefreshToken 一般放在 redis 吗
    daimubai
        39
    daimubai  
       2021-12-10 22:32:35 +08:00
    @Qsong 请教下,“当 token 过期后,后端会利用 refeshToken 返回一个新的 token 给到前端使用”,在什么时候返回呢。是后端返给前端 403 ,然后前端去调获取新 token 的接口拿到新的 token ,然后再二次重试上一次的请求吗
    daimubai
        40
    daimubai  
       2021-12-10 22:36:43 +08:00
    @Qsong 因为这个时候 token 已经过期了嘛,怎么知道用户对应的哪个 refreshToken 啊
    liuxu
        41
    liuxu  
       2021-12-16 22:13:28 +08:00   ❤️ 1
    今天我也遇到这个问题,所以详细看了下 oauth2 refresh token 的 rfc ,https://datatracker.ietf.org/doc/html/rfc6749#section-1.5

    简单的说,安全有等级,即使用 rsa 加密,是要不限制时间,你算个千百万年,也可以破解出来

    access token 有 scope 限制资源范围,它只用于用户和资源方交互(资源方转而和授权方交互),同时限制有效时间,这样它存储在用户 client 端,如浏览器中,所以即使被盗取后也只能被盗取有限的信息,攻击面很窄

    refresh token 一般只存储在绝对安全的环境,如用户 server 端,而且它只用于和授权方交互,更新 access token 等

    oauth2 获取 access token 和 refresh token 都是强制要求 https 环境,也即是要保证信道安全,避免被网络劫持获取到

    同时 oauth2 强制要求 refresh token 使用时必须能验证使用者身份来源,避免被冒充,方案一般有限制 refresh token 只能使用一次,这样攻击者获取到使用后,你再使用授权方会通知你 refresh token 已被使用,这样就可以发现当前用户 server 环境有安全问题,需要修复
    或者限制 ip 白名单等
    ioufev
        42
    ioufev  
       2022-12-30 09:44:58 +08:00
    正好和人最近讨论这个 token 刷新问题。
    比如 token 有效期时 30 分钟,
    用户登录 20 分钟后,有操作,需要续期。
    否则用户正操作着呢,突然需要重新登录,体验太差了。
    续期操作分前端和后端两种方式。
    后端方式,简单粗暴就是将 token 的 redis 有效期重新设为 30 分钟,不过这样 token 自带的有效期就用不上了,不太推荐。
    前端方式,token 剩余 10 分钟时,使用旧的 token ,申请个新的 token 。
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   2501 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 29ms · UTC 00:28 · PVG 08:28 · LAX 16:28 · JFK 19:28
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.