TOC

密码安全

    在Web开发中,对于用户的密码一般都不会使用明文存储,最基本的都是经过md5加密后的密文,但是对于md5目前的安全性来讲,我们非常不推荐使用,常见的一些密码我们都可以通过md5彩虹表或者撞库的方式得到,技术发展到今天,有很多的强度较高的单向加密算法,我们可以采用,但是我们需要注意的是,这种所谓的单向加密算法,虽然是不可逆的,但是它最主要的问题是在于,加密速度非常的快,那么带来的问题是解密速度也过快,这很难防止暴力破解了,这是常见的单向加密算法最大的问题;
    所以为了安全考虑,我们应该选择一个即不可逆,在密码破解方面能够增加难度,比如解密耗费时间周期较长的算法,从而来保证我们的密码安全性,那么同时我们还需要考虑一个问题,就是密码雷同的问题,比如md5针对相同字符的加密得到的密文都是一样的,说明原文一样,也就是说破解了一个,就破解了一堆,那么在这个时候,我们就应该对每个密码加salt(盐),这就是salt(盐)策略,但是如果两个密码原文是一样的,salt(盐)也是一样的,那密文还是一样的,所以,这个时候,还需要salt(盐)能随机,每个密码都有一个独立的salt(盐),那么在这种场景下,即使原文一样,得到密文也不一样,这就是接下来要学习的bcrypt模块;

bcrypt

    bcrypt模块是一个用于在Python中生成强哈希值的库,bcrypt的算法使用强大的加密技术为我们创建哈希并加盐,它减慢了散列的速度,使暴力破解的尝试越来越难,随着计算机变得越来越快,最佳成本因数会随着时间而变化,高成本因素的缺点是增加了系统资源的负载并影响了用户体验,同时它也可以为每个原文生成一个专属的salt(盐),即使原文一样,也会得到不同的salt(盐),所以加密后的密文,即使原文是一致的,密文也不一致,具体使用如下;
import bcrypt

# 密码必须经过编码
password = "caichangen".encode()
# 测试,生成salt(盐),可以看到多次生成的salt都不一样
print(bcrypt.gensalt()) # b'$2b$12$njhiMtnU329F80x1fDxHm.'
print(bcrypt.gensalt()) # b'$2b$12$Ef8mHhHNLYcj2JV.3rN4Ce'

# 加密
new_password1 = bcrypt.hashpw(password, bcrypt.gensalt())
new_password2 = bcrypt.hashpw(password, bcrypt.gensalt())
print(new_password1) # b'$2b$12$hKYN/FcVkf9xAGEBnCIMP.SvJunLosJl06VSe6RhDOZVrNE0xdd6u'
print(new_password2) # b'$2b$12$gBLDrdXk6v6rV8lrxpV5QuVsJy4Bt.tjMeECynLatxDt2XeiTHfmC'
# 校验
print(bcrypt.checkpw(password, new_password1)) # True
print(bcrypt.checkpw(password, new_password2)) # True

JWT认证

    JWT全称Json Web Token,JWT是现在做Web开发中主流的一个JSON验证的解决方案,它是一个开源的,采用RFC 7519的标准协议,它定义了一种紧凑自包含的方式,用于在各方之间以JSON对象安全地传输信息,也就是说使用JWT,可以在Web开发中,应用于前后端的数据传递方式,或者可以用于两个独立系统模块之间的数据传递方式; 
    在一个Web系统中,一个用户登陆成功后,以往都会使用session来存储用户的登陆信息,因为HTTP协议本身是一种无状态的协议,而这就意味着如果用户向我们的应用提供了用户名和密码来进行用户认证,那么下一次请求时,用户还要再一次进行用户认证才行;
    因为根据HTTP协议,我们并不能知道是哪个用户发出来的请求,所以为了让我们的应用能识别是哪个用户发送的请求,我们只能在服务器存储一份用户登录的信息,这份登录信息会在响应传递给浏览器,告诉其保存为cookie,以便下次请求时发送给我们的应用,这样我们的应用就能识别请求来自哪个用户了,这就是传统的session认证;
    那么每个用户经过认证之后,我们的应用都要在服务端做一次记录,为方便用户下次请求的鉴别,通常而言session都是保存在内存中,而随着认证用户的增多,服务端的开销会明显增大,同时用户认证之后,服务端做认证记录,如果认证的记录被保存在内存中的话,这意味着用户下次请求还必须要请求这台服务器上,这样才能拿到授权的资源,这样在分布式的应用上,相应的限制了负载均衡的能力,这也意味着限制了应用的扩展能力;
    那么一个非常常见的安全问题来了,因为是基于cookie来进行识别的,cookie如果被拦截,用户就会很容易受到跨站请求伪造的攻击,这就是session的问题
    那么为了解决Session的问题,就出现了JWT的这种解决方案,JWT的认证方式完全是基于令牌的,而且是基于客户端的令牌存储,这个令牌是不存储在服务端的,也就是说,使用JWT的认证,首先就解决了服务端内存占用的问题,它的认证流程如下;
    用于发起登陆之后,服务端会生成一个Token,这个Token响应给浏览器之后,浏览器可以在本地的LocallStorge或者SessionStorege里,那么后面所有的操作,使我们浏览器都携带这个Token,HTTP服务器收到请求会验证这个Token,如果是一个有效的令牌则通过访问,反之拒绝访问,具体流程如下图;

JWT详解

    如下图,JWT主要由header、payload、verify signature三部分组成,最终形成左边的密文,它以"."分割成三部分,第一部分为header的base64编码后的结果,第二部分为payload编码后的结果,第三部分为签名密文,密文是基于header、payload的base64编码以自定义secret计算出来的密文;
    也就是说,如果header、payload或者自定义secret一旦有一点微小的变化都会引起密文巨大的改变,虽然,我们的header和payload能够通过base64进行解码,但是我们自定义的secret,一般都是具有私密性的,不对外公开,缺少了这个secret,任何人依旧无法得到JWT字符串,所以secret的隐私性非常重要;
    那么对于服务端,JWT有一个验签的过程,当浏览器将JWT字符串发送过来之后,首先会取出header和payload部分,然后结合secret,使用HMASCHA256加密算法进行加密,如果得到的结果和第三部分一致,那么就说明这是本系统颁发的令牌,也就是说,信息是没有被篡改的;
    如果在请求过程中,有用户拦截了这个请求,不管是把header改了,第一部分改了还是第二部分改了,还是第三部分改了,最后生成的结果,都不会和第三部分一致,因为secret恶意者拿不到;

JWT加解密

    对于Python来讲,JWT官方提供了pyjwt模块,供Python用户使用,主要有encode和decode两个方法,一个为加密,一个为解密,如下;
import jwt

header = {
    "alg": "HS256",
    "typ": "JWT"
}
payload = {
    "name": "cce",
    "role": "admin",
    "login": True
}
secret = '_9)0u1ciw^^-47j+i$=(&0kus+@u+)s6%wh$v6=@%h!1_hp)-i'
# 加密
enc = jwt.encode(payload, secret, headers=header)
print(enc) # b'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJyb2xlIjoiYWRtaW4iLCJsb2dpbiI6dHJ1ZSwibmFtZSI6ImNjZSJ9.7UbJkdfUv23OfgP_VE2EMR3FdnkKHazxuaZXAsmzUqE'
# 解密
print(jwt.decode(enc,key=secret)) # {'role': 'admin', 'login': True, 'name': 'cce'}
    可以看到,我们通过pyjwt完成了数据的一次加解密,但是我们需要知道的是,这个解密是没有任何意义的,这个解密只是进行了一次验签,因为payload是通过base64编码的,我们想要解码这个payload非常的简单,所以,解密没有任何意义,它只是一套防篡改的技术,只要我们将这三部分任何一部分进行了变化,都不会通过验签,因为我们的secret是私密的,任何人都得不到;
    即使恶意者修改了第一部分的header,第二部分payload甚至修改了第三部分的signature,都没用,因为缺少了secret,最终服务端在验签的时候,通过私有的secret进行验签得到的结果,根本不可能和第三部分一致,所以这套系统,根本没有加密数据,只是加密了签名;
    因为,我们也不应该将一些私密数据放在payload里面,这是一套明文发送数据的技术,它只是防篡改,但它对数据本身并不加密,说到底,JWT不能进行敏感数据的传输,它只是防篡改的;
    所以JWT只是来验证身份的,只有在登陆成功,才会向浏览器返回这个Token,而且这个Token还不能修改,一旦修改,我们就能检测出来;

JWT过期时间

    JWT作为一个签名算法,它还提供了过期策略,JWT要求在payload里面加入键为exp,值为时间戳,该时间戳为过期时间的时间戳格式,那么当该JWT字符串在进行decode的时候,就会判断这个过期时间,是否大于当前时间,如果大于当前时间,则通过,否则,抛出异常,具体的不在此做过多的演示,详情请查看pyjwt官方文档;

JWT应用场景

    JWT最常见的应用常见就是用来做认证,一旦用户登陆成功就会得到JWT字符串,即Token,那么下次请求之后,带上这个Token,我们就可以验证这个Token是不是本HTTP服务器颁发的,一旦验证成功,就可以被允许访问资源,同时,它还可以在不同域名中传递,这一点主要的作用就是应用在单点登录上面;

发表回复

您的电子邮箱地址不会被公开。 必填项已用 * 标注