Python3中的hashlib提供了常见的摘要算法,如MD5、SHA1等等。
【概述】:什么是摘要算法呢?摘要算法又称哈希算法、散列算法。它通过一个函数,把任意长度的数据转换为一个长度固定的数据串(通常用16进制的字符串表示)。
【实质】:摘要算法就是通过摘要函数f(x)对任意长度的数据data计算出固定长度的摘要digest,目的是为了发现原始数据是否被人篡改过。

一、计算一段字符串的MD5码

import hashlib
md5 = hashlib.md5()
md5.update('I Love You'.encode('utf-8'))
md5_str = md5.hexdigest()  # 生成16进制摘要
print(md5_str)

执行结果:

cd2824bba5d2803793c0553c8f7c0bd1

如果输入的字符串过长,也可以这样,但结果和上面一样:

md5.update('I Love'.encode('utf-8'))
md5.update(' You'.encode('utf-8'))

【简洁版本】:

import hashlib
md5_str = hashlib.md5('I Love You'.encode('utf-8')).hexdigest()
print(md5_str)

二、摘要算法的应用

在数据库中我们经常会遇到要存储用户的用户名(username)和密码(password)的时候,如果我们直接将用户的口令密码的明文直接存储在数据库中,如下图:

ID

Username

Password

1

sparks_fly

spark123456

2

Mary

qq6411589




则网站的管理人员或数据库运维人员会轻而易举的获取每个用户的用户名和密码,这样用户的密码无疑被暴露出来。
如何解决的这样的问题呢?
此时可以用到刚才提到的MD5码,如下图:

ID

Username

Password

1

sparks_fly

78ffdf361a2e7746c848d5f2beed8e3f

2

Mary

a63b50c14bca6c461dac676f1e88d7cd




如果变成这样的存储方式,即使是管理员看到每个用户的数据库表也无法轻易知道用户的明文密码。这样,提高了用户口令存储的安全性

①使用hashlib

以下我们可以先从编写一个简单的注册用户信息的代码开始:

import hashlib
db = {}
def register(username,password):
    md5 = hashlib.md5()
    md5.update(password.encode('utf-8'))
    db[username] = md5.hexdigest()
    
register('sparks_fly','spark123456') 
register('Mary','qq6411589')
print('注册后数据库中写入的数据应该如下:')
for key in db:
    print('用户名:%-15s 密码:%s' % (key,db[key]))

执行结果:

注册后数据库中写入的数据应该如下:
用户名:sparks_fly      密码:78ffdf361a2e7746c848d5f2beed8e3f
用户名:Mary            密码:a63b50c14bca6c461dac676f1e88d7cd

然后我们在扩展到简单的登录功能:

import hashlib
db = {}
# 注册功能
def register(username,password):
    md5 = hashlib.md5()
    md5.update(password.encode('utf-8'))
    db[username] = md5.hexdigest()
# 登录功能
def login(username,password):
    md5 = hashlib.md5()
    md5.update(password.encode('utf-8'))
    if md5.hexdigest() == db[username]:
        print('%s登录成功' % username)
        return
    print('登录失败')
    
register('sparks_fly','spark123456') 
register('Mary','qq6411589')
login('Mary','qq6411589')

执行结果:

Mary登录成功

但是MD5码不是绝对安全的,比如有两个用户的密码是一样的,那么这两个密码生成的MD5码也一定是一样的。还有一种情况就是对于简单的密码,不怀好意的人可以先自己生成一个简单常用的密码生成的MD5库,然后对用户数据库中的MD5码进行match。因此可以想到在原有用户密码的基础上增加一些"东西"来增强安全性(这种方式被称为"加盐"),比如可以把用户的注册密码和用户名拼接成一个字符串再生成MD5码存储在数据库中。
代码如下:

import hashlib
db = {}
# 注册功能
def register(username,password):
    md5 = hashlib.md5()
    md5.update((password+username).encode('utf-8')) #拼接密码和用户名
    db[username] = md5.hexdigest()
# 登录功能
def login(username, password):
    md5 = hashlib.md5()
    md5.update((password+username).encode('utf-8'))
    if md5.hexdigest() == db[username]:
        print('%s登录成功' % username)
        return
    print('%s登录失败' % username)
    
register('sparks_fly','spark123456')  
register('Mary','qq6411589') 
print('注册后数据库中写入的数据应该如下:')
for key in db:
    print('用户名:%-15s 密码:%s' % (key,db[key]))
login('Mary','qq6411589')

执行结果:

注册后数据库中写入的数据应该如下:
用户名:sparks_fly      密码:5edc0043dfe3b0e8322e4b75e1e72130
用户名:Mary            密码:3c00f67fd930d2e04ddd600680662c19
Mary登录成功
②使用hmac

在上面的案例中,我们手动把用户名和密码进行拼凑,然后再生成MD5码,但是在python中提供了一种内建模块hmac可以帮我们做这件事,用此模块也会更标准。
代码如下:

import hmac
h = hmac.new(b'Mary', b'qq6411589', digestmod='MD5')
h.hexdigest()

# 执行结果:
'0c09ebc704b5887e51b1063e6aeca451'

语法:

hmac.new(key, message, digestmod='MD5')

这实际上就是Hmac算法:Keyed-Hashing for Message Authentication。它通过一个标准算法,在计算哈希的过程中,把key混入计算过程中。
此处传入的两个参数keymessage均是byte类型,所以除了可以在字符串前加b以外还可以对于字符串进行如下转换。

import hmac
user_b = bytes('Mary',encoding='utf-8')
password_b = bytes('qq6411589',encoding='utf-8')
h = hmac.new(user_b, password_b, digestmod='MD5')
h.hexdigest()