精华 cookie session,jwt,弱一致性数据与重放攻击
发布于 4 年前 作者 alsotang 15456 次浏览 最后一次编辑是 3 年前 来自 分享

TL;DR 版本:cookie session 与 jwt 中的数据,是比 server session 更加弱一致性的。

cookie session 与 jwt 的目的,都是为了在 scale 的时候,不 hit db。而是由 server 直接验证,提高 scale 的性能。

它们的缓存作用

首先需要明确一点的是,cookie session 与 jwt 都起着一定程度的缓存作用。

而缓存的目的,就是为了让读多写少的数据可以从架构更简单的存储服务中更高效地取出。

弱一致性与重放攻击的例子

重放攻击的本质也是数据的弱一致性。

两个场景,

一个是非法用户场景,这是个弱一致性的场景

比如一个用户,session 数据格式长这样

{name: 'alsotang', id: 1, is_valid: true, score: 20}

这时候,后端的运营同学发现该用户存在恶意行为,将改用户设成了 {is_valid: false}

可由于我们的 session 是存放在 cookie 中,server 并不知道这个用户已经被屏蔽了,那么 server 在解析了 session 后,会继续对用户放行。直到某些修改操作涉及 hit db,重新构造 session 为止。

一个是用户积分场景,这是个重放攻击的场景

假设用户要在我们的论坛发了个帖子,这个帖子需要扣除 20 积分,某个板块叫做【我是高级版块】,需要 10 积分才能进入。

发帖前的时间点是 A 时间点,对应的浏览器 cookie 为: cookie111111

发帖后,用户的数据长这个样子

{name: 'alsotang', id: 1, is_valid: true, score: 0}

这时是 B 时间点,对应的浏览器 cookie 被 server 更新为:cookie222222

那么这时,用户访问【我是高级版块】时,会被拒绝。

此时,用户把自己的 cookie 重设为 cookie111111

server 在接收到之后,解析出的用户 session 仍是

{name: 'alsotang', id: 1, is_valid: true, score: 20}

则顺利进入【我是高级版块】。

为何比 server session 更弱

因为 server session 可以被主动清除。

上述的场景1,运营同学在标记了 alsotang 为恶意用户后,运营后台的程序可以去缓存中主动删除用户的 session。

这时,用户在浏览器不关闭的情况下再次访问,server 会发现缓存中的 session 不存在了,于是从数据库中重新构造一个新 session。

上述的场景2,server session 不会受到类似的攻击。

当然,server session 也不是强一致性的,强一致性涉及事务,对于 server 整体的性能影响太大了。

举个板栗说明,

上述场景2中,用户是个爬虫程序,在同一秒内,不仅先回了帖,还后继发起 10 个请求,访问了【我是高级版块】的10个帖子。那么即使 get 的请求在 post 的请求之后,后者仍然是有机会全部通过的。这种属于牛角尖类型的例子,涉及财务的时候,才能发现它的威力。

再举个板栗,在 CNode 回帖后,帖子的作者是可以得到 5 个积分的。

假如 @i5ting 现在的积分是 5000 个,他正在浏览 CNode。我去回了一下他的帖子,现在他在数据库中是 5005 的积分。 而当他在浏览器不关闭的情况下,在首页看到自己的积分仍然会是 5000 个。其实我是可以在回帖之后去清除他 session 缓存的,但由于积分这个东西,从业务上说,追求个不精确的最终一致性即可。所以 server 也没有去清除。

如何最大程度避免

设计尽可能短的过期时间。

评估一下保存在 cookie session 与 jwt 中的数据经得起多久的不一致时间。

比如上述的场景1,1分钟应该问题不大。

你可以举出场景[1…100]出来,然后根据木桶定理,选择最短的时间作为过期时间。

15 回复

划重点~

涉及财务的时候,才能发现它的威力。

jwt只做基于token的用户鉴权就好了,没必要做其他的功能,jwt在加密解密,以及设置账号超期等有优势,所以特别适合api的授权

@captainblue2013 看到蓝神我就滚进来了

session 是存在server 中的,不是存在cookie

对于 @alsotang 提出的2个问题,cookie session好像是没好的办法解决。 不过像类似用户积分这种经常变化的数据,还是不要放在cookie session中了,就算放在里面,在我们需要严格校验积分的时候还是要去数据库查一遍。

问个问题, 一直没搞懂jwt 的作用 例如 用户token 验证 1 在用户登录时输入用户名密码 , 服务器端 生成UUID 作为token 不用jwt 2 token 储存在cookie 里面, 每次提交api 需要携带token 3 api 每次请求后 服务器端 查询token 表 比对 token 有效期, 成功后得到用户信息, 等 返回需要的资源

不太明白 第一步如果用jwt 而不用UUID 好处在哪? jwt 具体有什么作用? 是不是jwt里面可以直接保存token登录有效等信息而减少查询数据库? 现在redis也不慢, 而且也不可能在jwt里面存太多信息, jwt解析后还是要差一下数据库得到用户信息, 具体jwt好处是什么? 没搞懂

@jinwyp 我也没发现 jwt 哪里好。

@DoubleSpout

不是cookie session没法解决, 而是这算使用不当, 我记得我们在cnodejs之前的帖子里讨论过吧. 把session当cache误用, 和session存哪边没关系, 就算是存在server端, 就常用的KV方式(cookie session id<->server store), 这样使用这两个问题同样存在.

比如is_valid, 同一个用户用两个设备生成了对应同一个id的两个不同session操作, 有一个触发了is_valid=false, 除非再生成一个id->session id的对应表去同时刷对应的session, 不然怎么确保一致? 这不是同样是弱一致性么? 至于cookie session正常的方案是需要验证签名(比如根据password和salt生成), 当用户is_valid的时候刷新一下salt让原先的不同session签名都失效, 至于性能, 那归cache管.

再比如score, KV方式的server session无非根据cookie id取出session解序列化, 页面操作完毕前序列化了再存进去, 怎么确保多请求里session的score操作是原子性的? 针对一个加减score的页面发起并发请求, 怎么确保这些session操作会乖乖的顺序存写? 而不是某个先取session的操作因为卡住或者耗时长保存session得比后操作score的请求慢把后面几个都给弄没了? 这不同样存在重放攻击么?

正确的方式还是操作数据库完之后去刷新缓存里的{user-id}:is_valid、 {user-id}:score. 让session的归session, 让cache的归cache.

@lyjyiran 这才是正解。

分数放session本身就是一个大问题。cookiesession一定要做加密,这样客户端拿到也没办法伪造和修改。并且一定要有过期策略。

@lyjyiran

至于cookie session正常的方案是需要验证签名(比如根据password和salt生成), 当用户is_valid的时候刷新一下salt让原先的不同session签名都失效, 至于性能, 那归cache管

这句中的 salt 怎么理解?全局的还是每用户不同的?

@lyjyiran @fengmk2 你们不认为 session 的作用就是缓存的作用吗?

这种存储转计算的方式,过期时间设的很短后,性能的优势就不明显了,就是扩容的时候确实方便了不少

@zhang-ning 好久前写的东西。。听你这么一说,好像是这么回事

回到顶部