JWT秘钥案例分享

极客

JWT秘钥案例分享:一次线上事故让我彻底搞懂了它

大家好呀,我是你们的老朋友,一个在代码世界里摸爬滚打了七八年的后端程序员,今天想跟大家聊聊一个让我又爱又恨的话题——JWT秘钥案例分享,说真的,要不是上个月那场线上事故,我可能到现在还对JWT秘钥这件事一知半解呢,唉,说起来都是泪啊,但也正因为踩了坑,才想着把这段经历写出来,算是给自己的一个复盘,也给正在看这篇文章的你提个醒。

JWT秘钥案例分享

事情的起因:一个“不起眼”的配置

事情是这样的,我们团队负责的一个用户中心系统,认证模块一直用的是JWT(JSON Web Token),这玩意儿用起来是真香啊,无状态、跨服务方便,前后端分离的项目里简直是标配,当时项目上线也挺顺利的,大家都没太在意那个签名用的秘钥——就是那个在配置文件里躺着的字符串。

我记得特别清楚,那天下午我正悠哉悠哉地喝着咖啡,突然运维群里炸了锅:“有用户反馈,登录之后能看到别人的订单信息!”我一口咖啡差点喷出来,赶紧打开电脑,排查了一圈,发现问题竟然出在JWT秘钥上,你敢信?就因为秘钥的问题,导致token可以被伪造,攻击者随便改改payload就能冒充别人,那一刻我真的是后背发凉,心想这下完了。

深入排查:原来秘钥这么脆弱

后来我们复盘的时候才发现,问题其实早就埋下了,当时为了图方便,秘钥用的是开发环境里随手写的一串secret123这种弱口令,更离谱的是,测试环境和生产环境用的还是同一个秘钥!这就相当于你家大门钥匙和公司大门钥匙是同一把,丢了哪一把都是灾难。

这里插一句啊,很多小伙伴可能觉得“JWT秘钥嘛,随便设一个就行了,反正又没人知道”,拜托,这种想法真的太危险了!现在的攻击者可比我们想象的精明多了,他们专门有工具去跑弱秘钥字典,secret、123456、jwt这种,几秒钟就能爆破出来。

我们后来做了个统计,发现团队里竟然有三个项目的JWT秘钥是硬编码在代码里的,还有一个项目直接把秘钥写在了前端能访问到的配置文件里,哎呀妈呀,这不等于把家底儿都亮给人家看了吗?

解决方案:从“裸奔”到“全副武装”

痛定思痛,我们花了一周时间,把所有项目的JWT秘钥管理重新梳理了一遍,这里也跟大家分享一下我们的做法,算是JWT秘钥案例分享里比较实用的经验吧。

第一,秘钥必须足够复杂

我们现在的秘钥是用的openssl rand -base64 64生成的,64字节的随机字符串,暴力破解基本不可能,而且每个项目、每个环境都用了完全不同的秘钥,生产环境的秘钥只掌握在少数几个人手里,通过密钥管理服务(KMS)下发,再也不写在代码或配置文件里了。

第二,秘钥要定期轮换

这一点特别重要!我们现在的策略是每90天自动轮换一次秘钥,轮换的时候采用双秘钥机制,新老秘钥同时存在一段时间,保证已经签发的token还能正常验证,等所有旧token都过期了,再把老秘钥彻底删掉,这样即使秘钥不小心泄露了,攻击窗口也只有90天。

第三,加上算法白名单校验

之前我们用的库默认支持none算法,攻击者可以把header里的alg改成none,直接绕过签名验证,现在我们强制指定只允许HS256或RS256,并且把服务端接受的算法写死在代码里,任何其他算法直接拒绝。

那些容易被忽略的细节

说到这里,我还想啰嗦几句,在这次JWT秘钥案例分享的复盘中,我们还发现了几个特别容易被忽略的点:

  1. 日志里千万不要打印完整的token,我们之前有个同事调试的时候,把token直接打到日志里了,结果日志被采集到ELK平台,相当于token满天飞,后来我们加了脱敏处理,只打印前几位。

  2. 秘钥不要通过环境变量传递,虽然比硬编码强一点,但环境变量在某些场景下也会泄露,比如容器镜像里、CI/CD的构建日志里,我们后来统一改成了从KMS拉取。

  3. JWT的过期时间要合理,之前我们设的是7天,太长了,现在access token设15分钟,refresh token设7天,并且refresh token也要绑定设备指纹。

写在最后:别让JWT秘钥成为你的软肋

好啦,今天的JWT秘钥案例分享就写到这里吧,说实话,写这篇文章的时候我心情还挺复杂的,既庆幸我们及时发现了问题,又后怕如果没发现会怎样,JWT本身是个好东西,但秘钥管理要是不到位,那就是在裸奔啊。

如果你也在用JWT,赶紧去检查一下你的秘钥:是不是太简单了?是不是硬编码了?是不是所有环境共用一个?别等到出事了才后悔,那时候可就晚啦。

如果你觉得这篇文章对你有帮助,欢迎分享给身边的同事朋友,也欢迎在评论区聊聊你遇到过哪些JWT的坑,咱们一起避雷,毕竟,代码这条路,谁还不是一边踩坑一边成长呢?加油吧,各位!

文章版权声明:除非注明,否则均为极客网安-咸鱼原创文章,转载或复制请以超链接形式并注明出处。

目录[+]