在功能完成之后,现在要进一步考虑保护我们的系统。
一般来说,你要考虑两方面的事情:
- 正常用户会不会搞崩你的系统?
- 如果有人攻击你的系统,你能撑住吗?
对于中小型公司来说,第一条不会是问题,对于大公司来说,就要两条都考虑
现在我们的系统最明显的漏洞就是:
- 任何人都可以注册
- 任何人都可以登录
用脚本来刷,很容易构造出每秒几千几万的请求
也就是说,万一有一个人,用 shell 脚本拼命给你发注册请求、登录请求,系统负载就会很高,而且这两个请求都会查询数据库,也就是说数据库负载也很高
这个时候,我们可以考虑,能不能限制住每个人发的请求数量?又或者,限制住系统处理的请求数量?
这就是限流
限流是最常见的保护系统的办法。限流有很多算法,但是都大同小异,后面在微服务架构部分会进一步讲解。
这里我们使用限流,限制每一个用户,每秒最多发送固定数量的请求。
所以问题来了:
-
我怎么认定谁是谁?尤其是在登录和注册这个接口里,都还没登录成功,我都不知道他是谁。
-
我怎么确定我限流的这个阈值应该是多少?每秒 100 还是每秒 200?
第一个问题的答案是:用 IP。也就是说我们的限流针对的是 IP。
IP 虽然并不能实际意义上代表一个人,但这已经是我们比较好的选择了。
更好的选择是用 MAC 地址或者设备标识符之类的,比如说 CPU 序列号,但是在 Web 端很少用。(不容易拿到)
APP 端就可以考虑用设备序列号。
当然,在使用 IP 的情况下,我们可能会误把不同的人看成是同一个人。但是只要我们限制的阈值不是很小,就不会有问题。
限流阈值应该是多少?
理论上来说,这应该是通过压测来得到的(面试回答这个)。比如说你压测整个系统,发现最多只能撑住每秒 1000 个请求,那么阈值就是 1000。
而我们是针对个人,搞不了压测。所以可以凭借经验来设置,比如说我们正常人手速,一秒钟撑死一个请求,那么就算我们考虑到共享 IP 之类的问题,给个每秒 100 也已经足够了。
如果我每秒处理 100 个请求,那第 101 个请求过来怎么办?
显然,只能拒绝了,也就是返回错误。
这个错误,不同公司有不同的规范。如果你自己决策的话,可以返回什么服务器繁忙之类的信息。
Gin 里面有很多限流插件,从功能和非功能特性上,它们都没有什么区别。
但是你们要小心并发问题。
滑动窗口算法,同时收到多个请求,可能超出窗口限制
为什么要基于 Redis 来实现呢?
因为你要考虑到整个单体应用部署多个实例,用户的请求经过负载均衡之类的东西之后,就不一定落到同一个机器上了。
因此需要 Redis 来计数。
现在的问题是,不管是用 JWT 还是 Session,一旦被攻击者拿到关键的 JWT 或者 ssid,攻击者就能假冒你。
HTTPS 可以有效阻止攻击者拿到你的 JWT 或者 ssid。
但是如果你电脑中了病毒,那 HTTPS 就无能为力。
所以我们就要想,在用户登录校验过程中,我得进一步判断,用这个 JWT/ssid 的人是不是原本登录的那个人。
目前做得好的都是使用二次验证,也就是给你发邮件、发短信等。但是也有一些比较初步但也好用的手段,那就是用登录的辅助信息来判断。
在你登录的时候,记录一下你当时登录的一些额外信息。比如说:
- 你使用的浏览器,对应到 HTTP 的 User-Agent 头部。
- 你的硬件信息——手机 APP 比较多见。
问题:能不能用 IP?
在登录校验的时候,比较一下你当次请求的这些辅助信息和上一次的信息,不一样就认为有风险。
不能,换个网络或地点 IP 就不同了
右图则是在登录校验的时候比较了 User-Agent 这个参数。
为此你需要改造两个地方:
- Login 接口,在 JWT token 里面带上 User-Agent信息。
- JWT 登录校验中间件,在里面比较 User-Agent。
检查公司的前端接口,而后加入限流功能。
-
可以考虑整个集群限流
-
针对核心业务的接口限流
-
针对不需要登录就可以访问的接口限流
-
为限流添加对应的监控和告警
- 刷新 Session 过期时间的几种可行的办法,你要能够深入分析不同做法的优缺点。注意,面试不是实践,不能说我记住最佳的就行,而是你要尽可能”水“时间,展示自己博闻广识。
- 增强登录的安全性:
- 怎么保护 Session id?主要还是要启用 HTTPS 协议,把 Cookie 的 Secure 和 HttpOnly 都设置为true。
- 怎么做到在 Session id 或者 JWT token 泄露之后保护住用户?要在登录的时候记录一下登录的附加信息,例如 User-Agent,在登录校验的时候一并校验。
- 如何保护 Web 服务? 针对 IP 限流、整个集群限流。后续你还会接触到更加多的保护措施。
- 注意:这一部分的内容在你们面试初级工程师的时候,是属于比较有技术含量的点了,你在面试前一定要试着按照自己的说话习惯,整理好对应的答案,写出来多读几遍。