栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

自刷新Token——前端部分

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

自刷新Token——前端部分

这次做这个东西可以是纯属无用的自嗨,甚至是什么的,希望大家嘴下留情,并且可以给我一些建议,因为对于token这一块实在并不是算很了解的,毕竟也是看别人博客慢慢学的,谁还不是一个学生呢(doge)

后端相关介绍交给【君语流年】同学:自刷新Token——后端部分

对于token,我相信很多人都很熟悉

在之前我和小伙伴使用了一套简单的token技术用于自己开发的网页应用

具体流程是:

  • 用户使用账号密码登录
  • 服务端返回的响应头中包含token字段,客户端获取并将其储存,在以后每次对话将会附带上这个token,用于鉴权和自动登录(当然这个token具有失效时间)
  • 客户端使用logout接口后,相应的token会失去权限
  • 为了适应多端登录,一个账号可以对应多个token

当然我了解到当access token失效后,还可以使用refresh token请求新的access token,refresh token在每次登录时候获得,而且不参与本地储存

但是这里就出现一个问题,如果我的access token设置的过期时间太短,我的refresh token就需要比较频繁的重新获取新的access token,这样增加了refresh token被恶意获取的风险;同理,如果我的access token过期时间设置太长,也会有被恶意获取的风险

当攻击者能够维持一个虚假的登录态或者是权限的时候,用户信息什么就像是对他敞开门户,他甚至可以做一些非法操作,这样对用户和开发者来说会很难受

所以,在某个普普通通的一天,我和我的好友【君语流年】突然灵机一动,为什么不让展示在用户层面的token都是一个无效的token

怎么让传输的token无效呢,那就让每次token都会自动刷新就好了呀(ps:说的简单)!

还要让前后端同步更新,但是也要考虑网络状况问题,所以我们想到用一个step去对齐前后端的 “ 步数 ”(token刷新的次数),前端只需要每次更新token和step,而后端需要根据前端反馈的step去同步刷新token(通俗点说就是按照前端的步数去迭代自己存的token),然后看看这个token是不是能对的上。这里有个新问题,我可不能去相信一个步数差在 1000000000000000000…+ 的token吧,所以我们规定步数差大于10就认为该token失效

好了,步数问题确定了,那我们token格式是什么呢,起初我们想到直接在headers里加step和token字段,但是这样很容易让人看出端倪,然后我们又想到使用token+step的方式拼接成一个新的token,但是这样其实和前者没两样

而且这里也有一个新的问题,我的token怎么去生成一个新的token呢。DING!灵光一现,我可以使用 MD5(token+step) 的方式生成一个sToken(stepToken,划重点),当然MD5已经并不是很安全(不了解的同学可以了解一下:https://cloud.tencent.com/developer/article/1805350),但是他快呀!这里有个伏笔,为什么我可以用MD5

这样生成一个sToken后我怎么知道步数呢,oh no,我还是得加上一个步数,所以现在变成sToken+step,感觉没做什么有用的工作,但还好已经把更新token的问题解决了,接下来怎么去让step不是那么“显眼”,同时这里有有个关键性问题:我的token都没了,我服务端怎么去提取用户信息(因为原本token中存了用户的账号、密码、权限,当然这个token是加密后的,【君语流年】又不傻)

这时候直接开始讨论(此处省略一万字讨论内容),加上那段时间一直在面试,刚学完计网,我谋生一计,我们可以使用非对称加密呀,wow,就像TLS那样,用一个RSA将我的token加密起来,这样谁都看不懂了,连我自己都看不懂,说干就干

token = RSA(tToken+sToken+step)(tToken,trueToken,真正的带着信息的可用的token)

这里又有个问题,我的公钥可以加密,但是私钥并不能加密,只能签名(这里作者没有,甚至于一点也不了解加密相关内容,从前面MD5也可以看出来,不过因为MD5加密后的内容并不直接暴露,所以暂且先采用MD5,同时也解释上文伏笔),所以我使用了两对密钥,密钥对称保留,前端拥有一个私钥、一个公钥,后端拥有相应的一个公钥、一个私钥。前端传给后端信息时用后端的公钥加密,后端传给前端信息时用前端的公钥加密,当然我们得保证后端拥有的两个密钥相对安全。

当用户登陆时,会拿到一个通过公钥加密的token,这时候我们使用对应私钥解密,将解密后的token拆分出来(tToken、sToken、step)并将其储存在内存中,切记不能存储在任何用户能够直观看到的地方,因为这里有几个关键点:

1、内存相对来说不容易查阅

2、一般前端代码都会进行minify、uglify,这样增加定位难度

3、储存在localStorage、sessionStorage等地方就没意义了,控制台可以直接看呀

然后每次请求使用更新函数(这里是挂载在axios的拦截器里),创建好新的token后放在headers里传给后台,这时候这个token相当于被消耗了,使用者看到的这个token就失效了,下一次再请求时根据内存中的三个元素创建新的token,当然后台和前端的逻辑是一样的,但是后台每次要去看前端step,并且根据前端的step去做相同步数的更新,前端整体流程如下:

① 用户登录 --> token(RSA)

② token(RSA) - RSA key -> tToken、sToken、step(存在内存中)

用户请求:

③ sToken = MD5(sToken+step)

④ step++(当然不是认知上的加一,可以是自己规定的一个跨步大小或某种规律)

⑤ token(RSA) = RSA(tToken+sToken+step)

然后后台拿到后根据自己存储的sToken和step对照前端的sToken和step来确定前端的这个sToken是否有效,有效的话就可以使用这次的tToken来进行用户检查和鉴权

本来以为解决了,但是后来发现TLS并不是每次都用RSA去对话,而是RSA+对称加密。后来了解到RSA很费时,对于请求来说,我们这样弄会花费很多时间,肯定会消耗不必要的资源甚至会给用户带来不好的体验

所以效仿我们的TLS,只对第一次获取token的时候使用RSA,正常对话使用对称加密(我们这里采用AES-CBC),对于CBC来说需要一个iv,我们可以使用后端规定的flag作为iv(通过headers传递,在用户登录时传输,前端储存在内存中,这个flag在后面也有用处)。由前端登录请求时产生一个随机数random-client,在headers里传给后台RSA(random-client),后台解密后也产生一个random-server,将random-client和random-server采用某种特定的方式组合起来,形成一个AES密钥,用这个密钥对生成的token进行加密,传给前端,并且将RSA(random-server)通过headers传给前端,前端解密后用同样的方式组合出这个AES密钥,对token进行解密,优化后前端的相关步骤如下:

用户登录:

① RSA(random-client) --> token(AES) + RSA(random-server)

② random-server + random-client --> AES key

③ token(AES) - AES key -> tToken、sToken、step(存在内存中)

其它步骤不变

我本以为这样就完了,结果【君语流年】又提出了新的问题,说他需要一个flag来记录对应的token(AES)和对应的AES key,一番讨论后我们决定将flag(自定义一个可以辨别的值)直接明文拼接在token(AES)前作为标志位,当然采用特定的拼接手法

所以token = flag + token(AES)

这就是最终的token的实现了,当然可能其中有许多bug以及不足,不过我个人对于这个过程还是很满意的,基本上是我和【君语流年】的互相碰撞思考了

BUG

这里我要主动披露一些bug,这也是我在完成中和完成后思考所得:

1、如果攻击者主动降低网络速率甚至中断网络,这个时候它可以从请求报文中获取token,这时候的token并没有被消费,可以被消费一次。这样攻击者循环往复等同于拥有了访问权限。但是这个访问权限只限于当前用户,这也是为什么用户要保护好自己的电脑不被奇奇怪怪的插件入侵

2、因为前端需要做登录态,所以攻击者可以在localStorage中获取token,当然也要在token未被消费前,也就是页面自动登录请求没有被后端处理前,但这种攻击只能获取一些无关数据,因为我们规定这个请求不会返回用户关键数据,但是可以拿到uid。这类攻击只拥有一次权限,因为攻击者不知道random组合方法以及step更新规则

这两者对于当前来说确实是比较容易实现的攻击方式,但是都有受限

当然所有的探讨都是针对以下基础:

1、Google Chrome不容易被其他软件攻击,其自身防御相对安全

2、攻击者相对于比较不易于查看前端混淆后的代码,以及分析代码逻辑,查看内存占用

3、开发者不喜欢“裸奔”

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/1002533.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号