hashlib模块

发布时间 2023-12-19 19:05:46作者: 苏苏!!

hashlib模块

(一)什么是摘要算法

  • Python的hashlib提供了常见的摘要算法
    • 如MD5
    • SHA1等等。
  • 摘要算法又称哈希算法、散列算法。
  • 它通过一个函数,把任意长度的数据转换为一个长度固定的数据串(通常用16进制的字符串表示)。
  • 摘要算法就是通过摘要函数f()对任意长度的数据data计算出固定长度的摘要digest
    • 目的是为了发现原始数据是否被人篡改过。
  • 摘要算法之所以能指出数据是否被篡改过,就是因为摘要函数是一个单向函数
    • 计算f(data)很容易,但通过digest反推data却非常困难。
    • 而且,对原始数据做一个bit的修改,都会导致计算出的摘要完全不同。

(二)摘要算法(MD5)

(1)加密数据----一次性加密(同一段数据)

#导入模块
import hashlib
#1.创建md5对象
md5=hashlib.md5()
#2.获取原始的数据,转为二进制数据
res='syh'
res=res.encode('utf-8')
#3.加密数据
md5.update(res)
#4.获取加密后的值
res=md5.hexdigest()
print(res)#df3c2a17b71f08f79902e738e6b7730c

# 摘要算法加密的特点是 : 相同的数据加密出来的加密串是一样的,不同的数据的加密串肯定是不一样的

(2)分次加密(同一段数据)

  • 如果数据量很大,可以分块多次调用update(),最后计算的结果是一样的:
import hashlib

md5 = hashlib.md5()
md5.update(b'how to use md5 in ')
md5.update(b'python hashlib?')
print(md5.hexdigest())

# 计算结果如下:
# d26a53750bc40b38b65a520292f69306
  • MD5是最常见的摘要算法,速度很快,生成结果是固定的128 bit字节,通常用一个32位的16进制字符串表示。

(2)获取加密数据

# md5加密模块
from hashlib import md5
# 如果需要十六进制的结果与二进制的结果之间的转换,需要的模块
import binascii

# 【1】准备数据

# 这里是字符串类型
data = '你好'

# 字符串转二进制数据方式一
encode_data = data.encode()
# 字符串转二进制数据方式二
# encode_data = b'你好'

# 【2】数据加密
# 构建md5对象
md5_obj = md5()
# 将数据更新到md5算法中进行数据加密 (参数为二进制数据的明文数据)
# (方法一):直接在加密算法中进行转码
# md5_obj.update("你好".encode("utf-8"))
md5_obj.update(data.encode("utf-8"))
# (方法二):先将明文数据进行转码,再传入到加密算法中
# md5_obj.update(encode_data)

# 【3】数据提取
# 拿到加密字符串 # 十六进制的结果
result_16 = md5_obj.hexdigest()
print(result_16)
# 7eca689f0d3389d9dea66ae112e5cfd7

# 拿到加密字符串 # 二进制的结果
result_2 = md5_obj.digest()
print(result_2)
# b'~\xcah\x9f\r3\x89\xd9\xde\xa6j\xe1\x12\xe5\xcf\xd7'

# 拿到加密字符串 # 十六进制的结果与二进制的结果之间的转换 (参数为result_16 或 result_2)
result_change = binascii.unhexlify(result_16)
print(result_change)
# b'~\xcah\x9f\r3\x89\xd9\xde\xa6j\xe1\x12\xe5\xcf\xd7'

(三)摘要算法升级之加盐

  • 任何允许用户登录的网站都会存储用户登录的用户名和口令。
  • 如何存储用户名和口令呢?
    • 方法是存到数据库表中:
name    | password
--------+----------
michael | 123456
bob     | abc999
alice   | alice2008
  • 如果以明文保存用户口令,如果数据库泄露,所有用户的口令就落入黑客的手里。
  • 此外,网站运维人员是可以访问数据库的,也就是能获取到所有用户的口令。
  • 正确的保存口令的方式是不存储用户的明文口令,而是存储用户口令的摘要,比如MD5:
username | password
---------+---------------------------------
michael  | e10adc3949ba59abbe56e057f20f883e
bob      | 878ef96e86145580c38c87f0410ad153
alice    | 99b1c2188db85afee403b1536010c2c9
  • 考虑这么个情况,很多用户喜欢用123456,888888,password这些简单的口令
  • 于是,黑客可以事先计算出这些常用口令的MD5值,得到一个反推表:
'e10adc3949ba59abbe56e057f20f883e': '123456'
'21218cca77804d2ba1922c33e0151105': '888888'
'5f4dcc3b5aa765d61d8327deb882cf99': 'password'
  • 这样,无需破解,只需要对比数据库的MD5,黑客就获得了使用常用口令的用户账号。
  • 对于用户来讲,当然不要使用过于简单的口令。
    • 但是,我们能否在程序设计上对简单口令加强保护呢?
  • 由于常用口令的MD5值很容易被计算出来
    • 所以,要确保存储的用户口令不是那些已经被计算出来的常用口令的MD5
    • 这一方法通过对原始口令加一个复杂字符串来实现,俗称“加盐”:
hashlib.md5("salt".encode("utf8"))
  • 经过Salt处理的MD5口令,只要Salt不被黑客知道,即使用户输入简单口令,也很难通过MD5反推明文口令。
  • 但是如果有两个用户都使用了相同的简单口令比如123456,在数据库中,将存储两条相同的MD5值,这说明这两个用户的口令是一样的。
    • 有没有办法让使用相同口令的用户存储不同的MD5呢?
  • 如果假定用户无法修改登录名,就可以通过把登录名作为Salt的一部分来计算MD5,从而实现相同口令的用户也存储不同的MD5。
  • 摘要算法在很多地方都有广泛的应用。
    • 要注意摘要算法不是加密算法,不能用于加密(因为无法通过摘要反推明文),只能用于防篡改,但是它的单向计算特性决定了可以在不存储明文口令的情况下验证用户口令。

(四)摘要算法模板

import hashlib


def get_md5_digest(salt,data):
    data = salt + data
    b_data = data.encode('utf-8')
    encrypted_data = hashlib.md5(b_data)

    return encrypted_data.hexdigest()

练习:

练习一:

  • 注册
    • 保存加密密码到json文件中
  • 登录
    • 验证json文件中的加密文件是否和登录输入的加密密码一样
"""md5加密"""
def encrypt_password(data):
    #创建md5
    md5=hashlib.md5()
    #获取数据 转为二进制
    data=data.encode('utf-8')
    #对数据进行加密
    md5.update(data)
    #获取加密数据
    data=md5.hexdigest()
    return data
def save_data(path,data,mode='w',encoding='utf-8'):
    with open(path,mode=mode,encoding=encoding)as f:
        json.dump(data,f,ensure_ascii=False)
def register():
    username=input("请输入用户名:").strip()
    password=input("请输入用户名:").strip()
    encrypt_password_data=encrypt_password(data=password)
    user_pwd={"username":username,"password":encrypt_password_data}
    save_data(path='user_pwd',data=user_pwd)

def code(x):
    code=''
    for i in range(x):
        list=[str(random.randint(0,9)),chr(random.randint(97,122)),chr(random.randint(65,90))]
        code_choice=random.choice(list)
        code+=code_choice
    return code
def read_data(path,mode='r',encoding='utf-8'):
    with open(path,mode=mode,encoding=encoding)as f:
        data=json.load(f)
    return data
def login():
    username=input("请输入用户名:").strip()
    password=input("请输入用户名:").strip()
    data=read_data(path='user_pwd')
    #将登录输入的密码加密
    md5=hashlib.md5()
    password=password.encode('utf-8')
    md5.update(password)
    encrypt_password =md5.hexdigest()
    #比较登录加密后的密码 和注册加密的密码
    if encrypt_password!=data['password']:
        return  "密码错误"
    #获取验证码
    choice_code=code(6)
    print(choice_code)
    #输入验证码
    code_input=input("请输入验证码:").strip()
    #比较验证码
    if code_input.lower()!=choice_code.lower():
        return "验证码有误!"
    return  f"{username}登录成功!"

练习二:

  • 注册
    • 加盐加密
    • 记得保存注册时候生成的未加密的盐
  • 登录
    • 将注册时生成的盐找出来
    • 将登录输入的密码与注册时候的盐进行加密
    • 比较登录注册加盐加密的密码
    • 相同登录成功!不同登录失败!
"""加盐加密"""
def code(x):
    code=''
    for i in range(x):
        list=[str(random.randint(0,9)),chr(random.randint(97,122)),chr(random.randint(65,90))]
        choice_code=random.choice(list)
        code+=choice_code
    return code

def encrypt(data,salt):
    encrypt_password=data+salt
    #获取md5
    md5=hashlib.md5()
    #获取二进制的数据
    encrypt_password=encrypt_password.encode('utf-8')
    #对数据进行加密
    data=md5.update(encrypt_password)
    #获取机密后的数据
    encrypt_password=md5.hexdigest()
    return encrypt_password
def  save_data(path ,data,mode='w',encoding='utf-8'):
    with open(path,mode=mode,encoding=encoding)as f:
        json.dump(data,f,ensure_ascii=False)

def  regsiter():
    username=input("请输入用户名:").strip()
    password=input("请输入用户名:").strip()
    #加密密码时加盐
    salt=code(4)
    # 对密码进行加密
    encrypt_password = encrypt(data=password,salt=salt)
    user_pwd={username:{'username':username,'password':encrypt_password,'salt':salt}}
    save_data(path='user_pwd.json',data=user_pwd)
# regsiter()
def read_data(path,mode='r',encoding='utf-8'):
    with open(path,mode=mode,encoding=encoding)as f:
        data=json.load(f)
    return data
def login():
    username=input("请输入用户名:").strip()
    password=input("请输入用户名:").strip()
    #生成随机六位验证码
    choice_code=code(6)
    print(choice_code)
    code_input=input("请输入验证码:").strip()
    #比较验证码
    if choice_code.lower()!=code_input.lower():
        return f"验证码有误!"
    #读取json文件中的数据
    user_pwd_dict=read_data(path='user_pwd.json')
    user_pwd=user_pwd_dict.get(username)
    #获取文件中注册时存入的盐
    salt=user_pwd.get('salt')
    #将登录输入的密码和注册时的盐进行加密
    #{"username": "syh", "password": "60e6a3d07d40c5d79a6a24b7975d001a", "salt": "r15h"}
    password=encrypt(data=password,salt=salt)
    #将登录输入的加盐加密后的密码与注册时加盐加密的密码比较
    if password!=user_pwd.get('password'):
        return f"密码错误"
    return f"登录成功!"
print(login())

# 请输入用户名:syh
# 请输入用户名:123
# hC07Hq
# 请输入验证码:hC07Hq
# 登录成功!