ruby-on-rails 即使签名的最后一个字符发生更改,也可以进行JWT令牌解码

tmb3ates  于 2023-08-08  发布在  Ruby
关注(0)|答案(1)|浏览(80)

我刚刚在Rails应用程序上试用了JWT令牌,使用了这个jwt库:https://github.com/jwt/ruby-jwt

JWT.encode({sss: "333"}, 'SECRET_KEY')

字符串
返回下面的token:
eyJhbGciOiJiUzI1NiJ9.eyJzc3MiOiIzMzMifQ.CwX_1FztYHVpyx_G27u938SceilsVc5AB5Akwqlo2HA
然后我用上面的记号解码

JWT.decode("eyJhbGciOiJIUzI1NiJ9.eyJzc3MiOiIzMzMifQ.CwX_1FztYHVpyx_G27u938SceilsVc5AB5Akwqlo2HA", 'SECRET_KEY')


正确返回下面的响应:
[{“sss”=>“333”},{“alg”=>“HS256”}]
但是如果我尝试将令牌的最后一个字母改为B而不是当前的A,它仍然返回相同的响应,这很奇怪。

JWT.decode("eyJhbGciOiJIUzI1NiJ9.eyJzc3MiOiIzMzMifQ.CwX_1FztYHVpyx_G27u938SceilsVc5AB5Akwqlo2HB", 'SECRET_KEY')


即使我提供的token是错误的,也得到了这个响应:
[{“sss”=>“333”},{“alg”=>“HS256”}]
事实上,我得到了相同的React,所有字符到'D'
如果我使用F和上面的其他,那么它的显示错误就像预期的那样:

JWT.decode("eyJhbGciOiJIUzI1NiJ9.eyJzc3MiOiIzMzMifQ.CwX_1FztYHVpyx_G27u938SceilsVc5AB5Akwqlo2HF", 'SECRET_KEY')


JWT::VerificationError(签名验证引发)from(irb):34
这可能是什么原因呢?这是预期的行为还是我做错了什么?

6l7fqoea

6l7fqoea1#

原因是base64 url编码。JWT的三个部分都是base64 url编码的。Base64 encoding将输入数据转换为6位表示,Map为一组64个ASCII字符。如果你有3个字节的源数据(24位),base64编码的结果是4个字符长,每个字符代表一个6位值,所以4 * 6位= 24位。如果需要编码的位数不能被6除而没有余数,则会有一个字符具有2或4个无关紧要的位。
在您的示例中,编码签名有43个字符,这意味着43 * 6 = 258位。所以理论上你可以编码258位,但签名只有256位(32字节)长,这意味着结尾有2个无关紧要的位。
查看base64编码表可以看出,'A'到'D'代表6位值0(000000)到4(000011),所以前四位仍然有效,都是相同的,只有最后两个不重要的位在改变。但是字符'E'代表5(000100)并且将改变256位值的最后一位。
下表说明了这一点。它显示签名的最后4个base64字符,包括最后一个字符(A-D)的可能变化以及原始数据的位和字节数:x1c 0d1x该范围内最后一个字符的变化只会导致最后两位(浅灰色)的变化,但不会改变原始数据,因为改变的位超出了原始数据的最后一位。
如果你真的很担心末尾的2位,你可以考虑将签名算法改为HS 384。
然后,您将得到一个384位(= 48字节)的哈希,它用64个Base64字符表示。384可以被8和6除而没有余数,因此在末尾没有无关紧要的位,并且对最后一个字符的任何改变都将导致失败的验证。
HS 512将具有与HS 256相同的“问题”,并且在末尾甚至有4个无关紧要的比特,但仍然有更长的散列(512比特vs. 384位与256位)被认为是更安全的。

**结论:**一切都很好,这里没有错。签名的验证基于其二进制值,这不受编码特性的影响。如果你担心的话,你可以改变算法,但我认为这不是真的必要。算法的选择不应该基于此。

相关问题