Cookie, Session, token及JWT伪造

Cookie, Session, token及JWT伪造

前言:HTTP是一种无状态的协议,而这就意味着如果用户向浏览器提供了用户名和密码来进行用户认证,那么下一次请求时,用户还要再一次进行用户认证才行。为了分辨链接是谁发起的,需要浏览器自己去解决这个问题。而CookieSessionToken就是为了解决这个问题而提出来的机制。

Cookie是服务端发送给客户端的用于验证某一会话信息的数据,Cookie中有很多字段。不同网站Cookie中字段是不一样的,是由服务器端设置的。Cookie中常放入session id或者token用来验证会话的登录状态。

Cookie的分类

当我们打开一个浏览器访问某个网站的时候,该网站服务器会返回一个session cookie,当我们继续访问该网站下其他页面时,用该Cookie验证我们的身份。所以我们不需要每个页面都登录,但是我们关闭浏览器重新访问该网站时,需要重新登录获取浏览器返回的Cookiesession cookie在访问一个网站的过程中一般是不变化的,有时也会变化,比如切换不同的权限时Cookie值会变化。
浏览器生成session cookie的过程如下:
浏览器第一次发送请求(用户名和密码)给Web服务器,Web服务器把用户的登陆信息存在Cookie中发送给浏览器,浏览器再次访问该网站携带Cookie,直接访问。

是保存在客户端浏览器上存储用户登录信息的数据,是由服务端生成发送给浏览器的。浏览器会将Cookie保存在某个目录下的文本文件中,下次请求同一网站时就发送该Cookie给服务器。前提是浏览器设置开启了Cookie

Session

session是保存在服务器端的经过加密的存储特定用户会话所需的属性及配置信息的数据。当我们打开浏览器访问某网站时,session建立,只要浏览器不关闭(也有时间限制,可以自己设置超时时间),这个网站就可以记录用户的状态,当浏览器关闭时,session结束。
SessionID除了可以保存在Cookie中外,还可以保存在URL中,作为请求的一个参数(sid)。其中,保存在Cookie中的SessionIDsession cookie类似

Token

Session的弊端

上文提到Session的信息是保存在服务器端的,随着不同客户端用户的增加,独立的服务器已无法承载更多的用户,这时候Session的问题就会暴露出来:
1.每个用户经过应用认证之后,我们的应用都要在服务端做一次记录,以方便用户下次请求的鉴别,通常而言Session都是保存在内存中,而随着认证用户的增多,服务端的开销会明显增大。
2.用户认证之后,服务端做认证记录,如果认证的记录被保存在内存中的话,这意味着用户下次请求还必须要请求在这台服务器上,这样才能拿到授权的资源,比如说用两个机器组成一个集群,小明通过机器A登录系统,那么SessionID就会保存在机器A上,假设下一次请求被转发到机器B怎么办?机器B没有小明的SessionID。有时候会采用session sticky的小技巧,就是让小明的请求一直粘连在机器A上。但是这也不管用,要是机器A挂掉了,还得转到机器B上去。这样的话就只好做SessionID的复制了,即把A机器上的SessionID复制到B机器上。后来memcached提出把SessionID集中存储在一个地方,所有机器都通过这个地方来访问。但是这样一来,一旦负责Session的机器挂掉,所有人就得重新登录一遍,这种问题也是不愿意发生的。
3.CSRF攻击: 因为Session是基于Cookie来进行用户识别的,如果被截获,用户就会很容易受到跨站请求伪造的攻击。

token的建立

基于以上的问题有人提出了摆脱Session的想法。具体就是不在服务端保存SessionID了,让客户端去保存一个服务端生成的token,每次请求的时候附加上这个token,服务端只需要对这个token进行相应的校验就可以完成身份的验证了。
由于服务端不再存放SessionID了,也不会有token,那么可能就会有攻击者伪造token来攻击。因此服务端需要对token做一些防伪造的的处理,具体就是对数据做一个签名。譬如用HMAC-SHA256算法加上一个只有服务器知道的密钥,对数据做一个签名,然后把签名和数据一起作为token,由于密钥别人不知道,就无法伪造token了。服务器并不会保存这个token,当再次将token发到服务器的时候,服务端会用同样的HMAC-SHA256算法和同样的密钥去对数据再计算一次签名,并和token中的签名做一个比较,如果相同的话,就可以判断出已经登陆过,并且可以直接取到userId;如果不相同,则数据部分肯定被人篡改过,这时就能够做一些身份校验失败的相应处理。
这样一来,服务器就不需要保存SessionID了,只需要生成token,然后校验token,相当于用CPU计算时间换回了存储空间。

基于token的鉴权机制

基于token的鉴权机制类似于http协议也是无状态的,它不需要在服务端去保留用户的认证信息或者会话信息。这就意味着基于token认证机制的应用不需要去考虑用户在哪一台服务器登录了,这就为应用的扩展提供了便利。
流程上是这样的:
1.用户使用用户名密码来请求服务器
2.服务器进行验证用户的信息
3.服务器通过验证发送给用户一个token
4.客户端存储token,并在每次请求时附送上这个token
5.服务端验证token值,并返回数据
这个token必须要在每次请求时传递给服务端,它应该保存在请求头里, 另外,服务端要支持CORS(跨来源资源共享)策略,一般我们在服务端这么做就可以了。

JWT的构成

JWT是由三段信息构成的,将这三段信息文本用.连接在一起就构成了JWT字符串,譬如:

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SP0R2USEDHqPV7mcIK08ZAs4WtPMQ0NdMHuSD8tnWOw

头部(header)

JWT的头部承载两部分信息:
1.声明类型:这里是JWT
2.声明加密算法:通常使用HMAC SHA256
譬如上文第一部分的头部即为:

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

的base64加密后的字符串。
JWT支持将算法设定为“None”。如果“alg”字段设为“ None”,那么JWT的第三部分会被置空,这样任何token都是有效的。这样就可以伪造token进行随意访问。

载荷(payload)

存放有效信息,包含标准中注册的声明、公共的声明、私有的声明

标准中注册的声明 (不强制使用)

iss: jwt签发者
sub: jwt所面向的用户
aud: 接收jwt的一方
exp: jwt的过期时间,这个过期时间必须要大于签发时间
nbf: 定义在什么时间之前,该jwt都是不可用的.
iat: jwt的签发时间
jti: jwt的唯一身份标识,主要用来作为一次性token,从而回避重放攻击。

公共的声明

可添加任何信息,一般添加用户的相关信息或必要的信息,不建议添加敏感信息。

私有的声明

私有声明是提供者和消费者共同定义的声明,不建议存放敏感信息。
譬如上文第二部分的载荷即为:

{
  "sub": "1234567890",
  "name": "John Doe",
  "iat": 1516239022
}

的base64加密后的字符串。

签证(signature)

JWT第三部分是一个签证信息,由三部分组成:** header** (base64加密后的)、payload (base64加密后的)、secret
这个部分需要base64加密后的header和base64加密后的payload使用.连接组成的字符串,然后通过header中声明的加密方式进行加盐secret组合加密,然后就构成JWT的第三部分。
譬如上文第三部分的签证即为:

HMACSHA256(
  base64UrlEncode(header) + "." +
  base64UrlEncode(payload),
  your-256-bit-secret(我这里使用的是1234)
)

加盐secret组合加密后的字符串。
注意secret是保存在服务器端的,JWT的签发生成也是在服务器端的,secret就是用来进行JWT的签发和JWT的验证,所以,它就是你服务端的私钥,在任何场景都不应该流露出去。一旦客户端得知这个secret,那就意味着客户端是可以自我签发JWT了。

通过JWT进行认证

JWT的认证

客户端接收服务器返回的JWT,将其存储在CookielocalStorage中。此后,客户端将在与服务器交互中都会带JWT。如果将它存储在Cookie中,就可以自动发送,但是不会跨域,因此一般是将它放入HTTP请求的Header Authorization字段中。当跨域时,也可以将JWT被放置于POST请求的数据主体中。
服务器每次收到信息都会对它的前两部分进行加密,然后比对加密后的结果是否跟客户端传送过来的第三部分相同,如果相同则验证通过,否则失败。
https://img-blog.csdnimg.cn/20200512130216836.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzQ1NTIxMjgx,size_16,color_FFFFFF,t_70
JWT本身包含认证信息,因此一旦信息泄露,任何人都可以获得令牌的所有权限。为了减少盗用,JWT的有效期不宜设置太长。对于某些重要操作,用户在使用时应该每次都进行进行身份验证。为了减少盗用和窃取,JWT不建议使用HTTP协议来传输代码,而是使用加密的HTTPS协议进行传输。

JWT安全问题

1.修改算法为none
2.修改算法从RS256到HS256
3.信息泄漏 密钥泄漏
4.爆破密钥

JWT伪造

第一步:将web服务器返回的JWT数据进行翻译,我这里使用的是这个https://jwt.io/ 在线工具(也可以用base64去解码),找到要伪造的内容。
第二步:使用工具爆破秘钥https://github.com/brendan-rius/c-jwt-cracker 该程序需要在Linux环境下运行。

工具的安装及使用

终端进入到虚拟机下该文件目录中,输入docker build . -t jwtcrack即可进行build,如果报错的话,证明你的权限不够,需要将用户添加到docker用户组,输入:

sudo groupadd docker
sudo gpasswd -a $USER docker
newgrp docker

后重启即可。
build结束后就可以运行了

docker run -it --rm jwtcrack +JWT

等待输出secret即可。
第三步:在https://jwt.io/ 在线工具中输入爆破出来的秘钥进行验证,如果第三部分的内容与之前一致,证明秘钥正确,就可以进行修改了,修改后再粘贴生成的JWT利用burpsuite抓包进行修改后发回到服务器中即可实现JWT伪造。

参考:
https://blog.csdn.net/qq_45521281/article/details/106073624
https://blog.csdn.net/whoim_i/article/details/104279181/

posted @ 2021-11-08 16:16  king_kb  阅读(2636)  评论(0编辑  收藏  举报