乐趣区

签名防篡改思想

引言

最近学 jwt 学疯了,我之前很少做梦的,这两天天天做梦。

梦到自己写 jwt,梦到制定http 3.0 规范,梦到 Github 封杀我。

可能是又到了一层境界。

最近看了好多的博客去研究 jwt,发现很多博客对jwt 存在误解,所以我特意去 jwt 的官方网站去学习,试图理解设计的思想。

jwt

请移步 jwt 官网。

jwtjson web token

与传统的 SESSION 认证方式不同的是,传统方式是 COOKIE 里面存放 JSESSIONID,浏览器根据JSESSIONIDSESSION,用户信息存放在 SESSION 里。

jwt则不同,令牌经过解密,得到的就是用户信息,所以完全可以不用SESSION,以减轻服务器的存储压力。

jwt由三部分组成:HEADERPAYLOADSIGNATURE

HEADER

头信息包括签名算法和令牌类型。

如下数据所示,签名算法为HS256(签名算法会在后面用到),令牌类型为JWT

{
  "alg": "HS256",
  "typ": "JWT"
}

将头信息使用 base64url 加密,注意不是base64!两者区别:difference-between-basic-and-url-base64-encoding

在线加密网站:base64 encode online

注意,为了方便阅读,将 json 添加换行与缩进,在标准的 jwt 中,需要去掉换行和空格再进行加密。

加密完成:eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9jwt的头部有了。

PAYLOAD

语言的最高境界就是不用翻译,这里就不翻译了,similar to data

这部分比较自由,官方文档是这样描述的:not mandatory but recommended

mandatory大家肯定认识,这个词都用了快两年了。

iss (issuer), exp (expiration time), sub (subject), aud (audience)
{
  "iss": "yunzhi_auth_service",
  "exp": "1554453501",
  "sub": "zhangxishuo1998"
}

对此 json 串进行 base64url 加密。

base64url说解密就解密,所以不建议在数据中添加敏感信息,总不能把密码放里吧?一个用户 id 足矣,就像我们过去的 SESSION 一样。

如果觉得 id 不合适,那就用用户名,反正保证 unique 和查询效率即可,给 username 这一列加上索引,B+树查询相当快。

httpSession.setAttribute(TeacherService.TEACHER_ID, persistTeacher.getId());

加密完成:eyJpc3MiOiJ5dW56aGlfYXV0aF9zZXJ2aWNlIiwiZXhwIjoiMTU1NDQ1MzUwMSIsInN1YiI6InpoYW5neGlzaHVvMTk5OCJ9jwt的数据有了。

SIGNATURE

因为数据是透明的,所以假如用户把用户名给改了,再 base64url 加密,再去访问我们的系统,那不就变成了另一个用户登陆了吗?

为了解决数据篡改问题,引入了签名机制。

后台生成一个密钥用于签名,这里密钥以 yunzhi 为例。

数据是HEADER + . + PAYLOAD,即:eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9. eyJpc3MiOiJ5dW56aGlfYXV0aF9zZXJ2aWNlIiwiZXhwIjoiMTU1NDQ1MzUwMSIsInN1YiI6InpoYW5neGlzaHVvMTk5OCJ9

使用 HEADER 里设置的 HS256 算法和密钥 yunzhi 对数据进行签名。

这里不知道是不是我用的有问题,我用在线工具签出来的签名一直是错误的。但是我检查了好几遍,我就是和官网相同的步骤。很疑惑。

jwt官网签出来的签名是KlmSnbhfw8Q2RTB3KmOhFJcFxwzxJEcYa5osOlYP-5U

连接

HEADERPAYLOADSIGNATURE.连接在一起,就是jwt

jwteyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJ5dW56aGlfYXV0aF9zZXJ2aWNlIiwiZXhwIjoiMTU1NDQ1MzUwMSIsInN1YiI6InpoYW5neGlzaHVvMTk5OCJ9.KlmSnbhfw8Q2RTB3KmOhFJcFxwzxJEcYa5osOlYP-5U

签名

这里一起研究一下签名,签名是防止数据篡改的。

因为签名需要一个密钥,这个密钥是服务器私有的,所以能保证这个签名不被伪造。只有服务器能签名。

校验时,服务器再根据自己的密钥生成签名,对比,如果一致,说明数据没有被篡改。

SSL其实也是有签名的,其实就是我们常见的 HTTPS 证书。

RSA非对称加密,公私钥,私钥存储再服务器中,公钥要传输给浏览器。怎么保证传输过程公钥不被篡改呢?

如果公钥被黑客篡改,那就毫无安全性可言。

为了保证公钥的安全,设计了 HTTPS 证书。

CA机构将公钥进行 hash,生成摘要,再用自己的私钥对摘要进行签名,即生成数字签名。HTTPS 证书,就是公钥 + 数字签名。

浏览器获取到证书,同样对公钥进行 hash,生成摘要,用CA 公钥对数字签名解密,如果两者一致,证明证书没有被篡改。

总结

其实学习这个有啥用呢?如果只是停留在 jwthttps的使用上,那确实没什么用。

学习的是一种思想,一种前人经过无数次推敲总结出来的设计思想。

如果以后我们再碰到一个防止数据篡改的业务,有了这种思想,我们也可以设计自己的数字签名。

退出移动版