JWT?我一直都是这么理解的......

刚才看了learnku的另一篇文章:Laravel项目被入侵了,请问该如何应对和解决呢?,题主怀疑是系统的Token泄露导致的,JWT版本号是"tymon/jwt-auth": "^1.0",然后我去本地看了看我的依赖,发现和题主的组件和版本一样。

于是我的担忧来了……所以!无论如何要避免此类事情发生!

JWT组成

众所周知,JWT是由headerpayloadsignature三者通过.拼接生成的。

JWT?我一直都是这么理解的......
如图所示

  • 红色部分代表 header,主要存放JWT的加密方式等核心,由base64加密形成。
  • 紫色部分代表 payload,主要存放用户态信息,JWT过期时间等,由base64加密形成。
  • 蓝色部分代表 signature,这个是根据header+payload的拼接再加上secret经过某种方式加密形成的(核心)。

JWT生成

本着学习的心态了解一下PHP如何生成JWT,一是重新了解一下JWT,二是想从原理上分析JWT可能存在的问题。

// 现在我有一个已生成的JWT字符串
$token = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c';

// 解析$token中第一段取出header
$header = '{"alg": "HS256", "typ": "JWT"}'
// 解析$token中第二段取出payload
$payload = '{"sub": "1234567890","name": "John Doe","iat": 1516239022}'

好了,JWT中前两个已经被base64解析出来了,前两个由于是base64方式加密,相当于明文传输,所以大家不要将敏感信息放到里面去,不然很容易被黑客抓包拦截到,从而获取或修改用户的敏感信息。
那么signature(JWT的核心校验)是怎么生成的呢?带着疑问我去了一趟jwt.io

JWT?我一直都是这么理解的......

发现生成规则后,自己动手丰衣足食……

然后我拿着刚才生成的headerpayload打开了在线密钥生成,假设我的密钥为:your-256-bit-secret,按照理论来说应该生成出来的值为:SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c以对应jwt.iosignature的值。

这里有一个大坑

JWT?我一直都是这么理解的......

我生成了个什么东西!!!!
然后经过一番周折发现无论如何生成的都是小写英文加数字的组合……不符合逻辑啊

实在不行了,我去下载了"thans/tp-jwt-auth": "^1.1"的compser包,看看别人怎么生成的。

不要问我为什么选这个包,我随便找的。。。

JWT?我一直都是这么理解的......

JWT?我一直都是这么理解的......
翻阅良久,终于找到生成signature的核心代码:hash_hmac,从没用过这个函数,然后在官方文档,简单看了一下介绍并分析了一下第四个参数:

  • true输出原始二进制数据
  • false输出小写 16 进制字符串。

然后焕然大悟,知道为什么我在在线密钥生成生成的数据对不上了,网站默认是生成的16进制的字符串。
然后继续向下走,发现生成原始二进制数据也对不上,没关系继续看源码,然后发现问题。

JWT?我一直都是这么理解的......

加密之后再次生成,发现还是对不上……
对比之后发现这里有一个小坑:

  • 应该生成:SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c
  • 实际生成:SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV/adQssw5c

经过翻阅源码发现,此base64并非原生base64,话不多说上图,删掉了等号,替换了加号和斜杠,不知道为什么这么做,莫非是是RFC规范这么做的?。
JWT?我一直都是这么理解的......
好了,这下就完全没问题了,拿到jwt.io校验一下,嗯通过了,不错。

总结

一直都在用JWT做接口授权,只是对JWT原理略有耳闻,还没有这么深度的分析过,经过这次对JWT的了解,发现最敏感的地方就在SECRET。如果SECRET没有泄露的话理应来说不会造成Token的安全风险,逆向的话更是无稽之谈。所以至于题主Token泄露是什么原因,排除服务器挂马、用户设备中毒、商业恶性竞争的话,我也没分析出来。不过大家在产品上线后一定要重新生成SECRET,防止SECRET泄露。今天也算没白活,又Get到一个知识点。:laughing:

本作品采用《CC 协议》,转载必须注明作者和本文链接
本帖由系统于 3年前 自动加精
《L01 基础入门》
我们将带你从零开发一个项目并部署到线上,本课程教授 Web 开发中专业、实用的技能,如 Git 工作流、Laravel Mix 前端工作流等。
《G01 Go 实战入门》
从零开始带你一步步开发一个 Go 博客项目,让你在最短的时间内学会使用 Go 进行编码。项目结构很大程度上参考了 Laravel。
讨论数量: 8

强!学习了!

3年前 评论
AloneUtopia

跟着学习了 :+1:

3年前 评论
自由与温暖是遥不可及的梦想

感谢 楼主为大家 分析 很详细 很到位

3年前 评论
邪恶的咖啡 (楼主) 3年前

JWT 要求的是Base64Url不是Base64。这篇文章解释了两者的区别。blog.csdn.net/lotus_2015/article/d...

3年前 评论
邪恶的咖啡 (楼主) 3年前

强👍,属实奥里给

3年前 评论

这个必须要加密才可以 使用得,前端加密,后端解密才可以啊! 直接放上去,就是找死啊!

3年前 评论
Mr-houzi 3年前

讨论应以学习和精进为目的。请勿发布不友善或者负能量的内容,与人为善,比聪明更重要!