为什么不要用 JWT 做登录认证 Posted by xmpace on 2020-04-05

很多小公司用 JWT 做登录认证,这篇文章来讲讲用 JWT 做登录的问题。

如图所示,Header 与 Payload 是明文的,尽管它们会用 Base64 编码,但编码并不是加密,所以仍然是明文,Signature 是服务端用 Secret 对 Payload 做哈希得到的签名,JWT 的安全性完全依赖于这个签名,只要其他人拿不到 Secret,那么想伪造一个签名就几乎是不可能的事。

开发人员最中意于 JWT 的地方在于,用 JWT 可以只做内存计算就能验证登录的合法性,而不需要像其它方案一样,使用缓存或数据库存储登录信息。

然而我们不能只看它的优点而不关注它的缺点,用 JWT 做登录有非常大的问题。

首先第一点是 JWT 没有吊销机制,假设某用户的 JWT 被泄露了,我如何使这个 JWT 失效呢?由于 JWT 的验证是纯内存计算,仅依赖 JWT 本身的信息,导致根本没法判断它是否已失效,我们只能被动地等它过期,仅此而已。将过期时间设短一点也许能稍微缓解一下这个问题。

第二点是应该如何保护 Secret 的安全。

我们知道 Secret 一旦泄露,那其他人就可以伪造 JWT,登录任意用户的账号。而使用 JWT 做登录认证的开发人员,存储 Secret 最常见的方式是 —— 写死在代码里。

有人可能会反驳,这有什么问题么?我们的代码安全性很高,代码托管在我们自己的服务器上,我们自己的服务器只能内网访问,黑客想黑进来简直白日做梦,能访问的只能是我们自己的开发人员!

然而,我想说的是,你就是防不了你自己的开发人员。所谓日防夜防,家贼难防,你的开发人员会一辈子在你这工作不跳槽?一旦他跳槽,他拷贝了这个 Secret…

甚至,我见过更坑爹的,有些小公司会把代码发布到自建的 Maven 私服上(Java 程序员),而这个私服竟然是可公网访问的,并且是默认用户名和密码…

因此,要安全可靠,还是要用正儿八经的 Token 方案,存储是少不了的,这方面就不细说了,可以参考我以前的文章 《登录Token如何设计》

好了,讲完用 JWT 做登录认证的漏洞后,我要替 JWT 说句公道话了,其实上面的问题压根不是 JWT 的锅!

因为 JWT 只是告诉了你一种验证身份的方法,但 JWT 可没规定你的 Secret 要写死啊。造成问题的本质原因还是出在开发人员的安全意识上,而不是 JWT 本身。

仔细想想,其实登录认证 token 的本质是为用户生成一个临时密码,每次用该临时密码传输请求,那么,你把 Secret 写死,实际上相当于所有用户用同一个临时密码,这风险就大大提高了。要安全可靠,还是得为每个用户生成不同的临时密码,因此,存储是不可避免的。

由此可见,基于 JWT 的登录认证方案,要改进安全性也很简单,把 Secret 随机化并存储起来就行了,不过这样改过之后,跟 《登录Token如何设计》 中的方案就没有两样了。