hashlib 与 hmac 模块

一、引入

1.什么是算法(Algorithm)?

  • 算法就是高效解决问题的方法

  • 一种算法就是用来解决一种特定问题的方法

比如从10000个从小到大排列且不连续数字里找出1234
普通方法就是用眼瞅,从左到右
运气好瞅一次,运气不好瞅一万次
于是就有了二分法,取出中间的值
左边的值一定比中间的值小
右边的值一定比中间的值大
于是找到1234所用的时间大大缩短

2.hash哈希算法

该算法接收传入的文本内容/一串字符,经过运算得到一串哈希值。

一串内容---->"hash"算法---->"hash"
  • md5

    • 摘要算法,结果是固定长度

    • 只要原材料一样,结果就一样

  • sha512

  • sha256

3.hash值的三大特点

  • 特点一:同一种hash算法下,传入的内容一样, 采用的 hash 算法一样, 得到的 hash 值也一定一样

  • 特点二:同一种hash算法下,hash 不可逆推, 不能通过 hash 值反推出明文

  • 特点三:同一种hash算法下,hash值的长度取决于采用的hash算法,无论校验的文件内容有多大,得到的hash值的长度是固定的。

4.哈希的应用

  • 结合特点一和二可以用于加密

  • 结合特点一和二可以用于文件完整性校验

    在网页上下载文件有时会卡在99%一段时间
    其实就是在进行”hash“值的校验
    

5.密码加盐

  • 客户端向服务端提交密码会在客户端先通过算法加密

  • 而服务端存有加密后的密码, 直接比对进行校验

  • 但是这还是不够安全的,高技术的黑客可以拿着密文密码进行撞库或者写客户端直接提交截获到的密文

  • 于是就出现了密码加盐的概念

    🛴密码加盐相当于暗号,只有设置者知道
    🦽示范:
        正常密码:2021123
        加盐密码:康2#0总2的1发1家2之3路
            或者:赵2总0的2辉1煌1人2生3
            
    🚲加盐之后计算出来的“hash”的完全不一样
    🍗想要撞库的人就必须要猜到使用的算法和加的盐
    

二、hashlib模块介绍

1.什么是hashlib模块

  • hashlib是python的内置模块

  • 它为我们提供了多种安全的摘要方法

2.hashlib模块的作用

  • 目前在大部分操作系统下, hashlib模块支持md5(),sha1(), sha224(), sha256(), sha384(), sha512(), blake2b()….等多种hash构造方法

  • 这些构造方法在使用上通用, 返回带有同样接口的hash对象, 差别只在于构造方法的选择, 也就是对算法的选择

  • 得到对象后可以使用通用的update()方法将bytes类型的数据添加到对象里,最后通过digest()(bytes摘要) 或者hexdigest()(十六进制摘要) 方法获得当前的摘要

🥗 "hashlib"模块所支持的算法
__always_supported = ('md5', 'sha1', 'sha224', 'sha256', 'sha384', 'sha512',
                      'blake2b', 'blake2s',
                      'sha3_224', 'sha3_256', 'sha3_384', 'sha3_512',
                      'shake_128', 'shake_256')

注意:update()方法现在只接收bytes类型的数据,不接收str类型,因为哈希基于字节而非字符。

三、hashlib模块的使用

1.放入原材料,得到摘要

import hashlib
# m = hashlib.sha512()
m=hashlib.md5("helloword".encode("utf-8"))
res=m.hexdigest()   #还有其他算法"sha256()"等等
print(type(res))    #<class 'str'>
print(res)  #59284aa85709ddaf3bd246030060f6a2

2.将原材料分多次 update( )

  • 把一段很长的数据update多次,与一次update这段长度的数据,得到的结果一样, 但是update多次为校验大文件提供了可能

    import hashlib# m = hashlib.sha512()
    m = hashlib.md5()  # 还有其他算法"sha256()"等等
    m.update("he".encode("utf-8"))
    m.update("llo".encode("utf-8"))
    m.update("word".encode("utf-8"))
    res = m.hexdigest()
    print(res)        # 59284aa85709ddaf3bd246030060f6a2
    
小结:
  • 注意1: m.update()传值可以理解为搬运传值。这种类型的好处是在于,可以校验大文件。但是不好的一点就是这种每一次的搬运基于直接使用下面的那种方式效率上, 还是有影响。

  • 注意2: hashlib.md5()中也能进行传值, 不过这种传值是一次性传值. 不好的一点就是不适合针对大文件的校验.

  • 注意3: 哈希值校验的是bytes类型, 我们针对字符串需要进行编码, 编码类型随意, 并不影响哈希值。

3.密码加盐

  • 以上加密算法虽然非常安全,但有时存在缺陷,黑客通过撞库可以反解, 所以, 有必要对加密算法中添加自定义key再来做加密

import hashlib🍛没加盐
m=hashlib.sha256()
m.update("helloword".encode("utf-8"))
res=m.hexdigest()
print(res)  #0b322d15ea034793a8646baa1744ffacbdf7f959b66c68970f032c4ac8b9c8cb🎂密码加盐
import hashlib
salt = "撒一把盐"
m = hashlib.sha256()
m.update("helloword".encode("utf-8"))
m.update(salt.encode("utf-8"))
res = m.hexdigest()
print(res)  # 0b322d15ea034793a8646baa1744ffacbdf7f959b66c68970f032c4ac8b9c8cb

可以发现两种结果不同,加的盐只有自己知道,从而降低被破解的几率就大大降低

三、hmac模块

1.什么是hmac模块

  • hmac 模块也是 Python 的内置模块

  • 它内部采用了 key 对应 内容 的方式进行进一步的加密方式

2.保证hmac结果一致

  • 必须保证 hmac.new( ) 括号内指定的初始 key 相同

  • 无论 update 多少次, 校验的内容累加一起必须一样

3.hmac的使用

  • 加密内容一次update

    import hmach = hmac.new("init_key".encode("utf-8"), digestmod="md5")
    h.update("helloword".encode("utf-8"))
    res = h.hexdigest()
    print(res)  # 7c221a51ee6e0d36a1a8b07d7948bc0c
    
  • 加密内容多次 update

    import hmac
    h = hmac.new("init_key".encode("utf-8"),digestmod="md5")
    h.update("he".encode("utf-8"))
    h.update("llo".encode("utf-8"))
    h.update("word".encode("utf-8"))
    res = h.hexdigest()
    print(res)  # 7c221a51ee6e0d36a1a8b07d7948bc0c
    

    可以发现摘要值结果一样

  • 将初始 key 更换, 加密内容不变

    import hmac
    h = hmac.new("neq_key".encode("utf-8"),digestmod="md5")
    h.update("helloword".encode("utf-8"))
    res = h.hexdigest()
    print(res)  # 200e7722b5842e83851955e20dabb6e8
    

    可以发现摘要值已经变化了

总结:
  • 使用hmac算法比标准hash算法更安全,因为针对相同的加密内容,不同的key会产生不同的hash

四、暴力破解

什么是暴力破解?其实就是乱猜. 暴力破解其实是通过循环的方式进行对比, 我们都知道加密算法是不可逆的, 比如 MD5 算法, 那么网上那些所谓的 MD5 解密网站是怎么做到的呢? 其实使用的就是这种原理.

撞库的原理也差不多 : 简单来说,撞库就是黑客利用已经泄露的账号密码,去其他网站或应用程序中尝试登录的行为。撞库主要利用的是人们在多个平台使用相同账号密码的行为习惯

import hashlibm = hashlib.md5()
m.update("123".encode("utf-8"))
print(m.hexdigest())  # 202cb962ac59075b964b07152d234b70

就比如我们通过 MD5 算法加密一段字符串 “123” 得到的密文是 “202cb962ac59075b964b07152d234b70” , 我们是无法看懂或者是解密的, 那网上的 MD5 是怎么做到的, 其实他们就是将一些简单的密码 : 123,abc, 123456, abc123,…..等等这种比较简单的低级密码都进行加密的一遍, 然后放在他们的数据库里边, 你在使用的时候恰巧就对比到一样的密文, 这就是 MD5 暴力破解

image-20210112155001593

稍微加一个@符号

import hashlibm = hashlib.md5()
m.update("1234@".encode("utf-8"))
print(m.hexdigest())  # 2119eb59afc81b22cf8a4298047f9723

image-20210112155428264

所以我们只要把密码设置复杂一点就不用怕MD5暴力破解了

历史背景:8月17日的美国加州圣巴巴拉,正在召开的国际密码学会议, 来自山东大学的王小云教授做了破译MD5、HAVAL-128、 MD4和RIPEMD算法的报告, 当时引发了密码学界的轩然大波, 于是MD5算法团队又对MD5算法进行了修改, 到现在为止还是比较安全的

五、模拟密文密码撞库

import hashlib, shelvewith shelve.open(r"./library.txt") as f:
    while True:
        strs = input("请输入明文进行密文入库(q退出)》》》:").strip()
        if strs == "q": break
        m = hashlib.md5()
        m.update(strs.encode("utf-8"))
        res = m.hexdigest()
        f[strs] = res
with shelve.open(r"./library.txt") as f1:
    while True:
        password_md5 = input("请输入密文进行解密(q退出)》》》").strip()
        if password_md5 == "q": break
        for k, v in f1.items():
            if password_md5 == v:
                print(f"明文密码:{k}")
                break
        else:
            print("解码不成功,请开通会员~~·")

输出内容如下:

请输入明文进行密文入库(q退出)》》》:123
请输入明文进行密文入库(q退出)》》》:康康
请输入明文进行密文入库(q退出)》》》:txx
请输入明文进行密文入库(q退出)》》》:123456
请输入明文进行密文入库(q退出)》》》:q
请输入密文进行解密(q退出)》》》2119eb59afc81b22cf8a4298047f9723
解码不成功,请开通会员~~·
请输入密文进行解密(q退出)》》》202cb962ac59075b964b07152d234b70
明文密码:123
请输入密文进行解密(q退出)》》》q

五、模拟网上下载大文件进行MD5值计算的快速方式

假设一个10G的大文件, 要计算整个文件的 MD5 值会比较慢, 那么我们可以截取固定位置的字节来进行校验, 这样就快了很多, 甚至可以加盐对比, 这样一来就可以防止他们从中间恶意串改文件, 也可以加快速度

import os,hashlib,shutil
def auth(path):
    file_size=os.path.getsize(path)
    with open(path,"rb") as f:
        f.seek(0)                  # 指针在开头
        res1 = f.read(3)           # 取三个字节
        f.seek(file_size//10,0)    # 指针在十分之一处
        res2 = f.read(3)           # 取三个字节
        m = hashlib.md5()
        m.update(str(res1+res2).encode("utf-8"))
        return m.hexdigest()       # 六个字节加密后返回
hash1 = auth(r"F:\python_16\day 33\撩妹必备.mp4")
print(hash1)  # 378e5e9de3ea4510455036d6248c07ce# 拷贝文件模拟文件下载,改变文件和路径
shutil.copyfile(r"F:\python_16\day 33\撩妹必备.mp4","./pwd.txt")
​
hash2 = auth("./pwd.txt")
print(hash2)  # 378e5e9de3ea4510455036d6248c07ce

由上可知, 拷贝之后的文件与源文件得到的MD5摘要完全相同(这里我只演示了两处截取字节, 也没有加盐), 说明校验文件成功, 文件完整

ps : 我们下载一个文件的时候经常在 90% 或者 100% 的时候卡住一段时间, 其实这就是在进行hash值的校验, 如果校验不成功甚至会重新下载