摘要算法简介
Python的hashlib提供了常见的摘要算法,如MD5,SHA1等等。
什么是摘要算法呢?摘要算法又称哈希算法、散列算法。它通过一个函数,把任意长度的数据转换为一个长度固定的数据串(通常用16进制的字符串表示)。
举个例子,你写了一篇文章,内容是一个字符串'how to use python hashlib - by Michael'
,并附上这篇文章的摘要是'2d73d4f15c0db7f5ecb321b6a65e5d6d'
。如果有人篡改了 你的文章,并发表为'how to use python hashlib - by Bob'
,你可以一下子指出Bob篡改了你的文章,因为根据'how to use python hashlib - by Bob'
计算出的摘要不同于原始 文章的摘要。
可见,摘要算法就是通过摘要函数f()
对任意长度的数据data
计算出固定长度的摘要digest
,目的是为了发现原始数据是否被人篡改过。
摘要算法之所以能指出数据是否被篡改过,就是因为摘要函数是一个单向函数,计算f(data)
很容易,但通过digest
反推data
却非常困难。而且,对原始数据做一个bit的修改,都会导致计 算出的摘要完全不同。
我们以常见的摘要算法MD5为例,计算出一个字符串的MD5值:
>>> import hashlib >>> md5=hashlib.md5() >>> md5.update('how to use md5 in python hashlib?'.encode('utf-8')) >>> print(md5.hexdigest())
结果如下
d26a53750bc40b38b65a520292f69306
如果数据线很大,可以分块多次调用update(),最后计算的结果是一样的
>>> import hashlib >>> md5=hashlib.md5() >>> md5.update('how to use md5 in '.encode('utf-8')) >>> md5.update('python hashlib?'.encode('utf-8')) >>> print(md5.hexdigest()) d26a53750bc40b38b65a520292f69306
试试改动一个字母,看看计算结果是否一样
MD5是最常见的摘要算法,速度很快,生成结果是固定的128 bit字节,通常用一个32位的16进制字符串表示。
另一种常见的摘要算法是SHA1,调用SHA1和调用MD5完全类似:
>>> import hashlib >>> sha1=hashlib.sha1() >>> sha1.update('how to use md5 in python hashlib?'.encode('utf-8')) >>> print(sha1.hexdigest()) b752d34ce353e2916e943dc92501021c8f6bca8c
SHA1的结果是160 bit字节,通常用一个40位的16进制字符串表示。
比SHA1更安全的算法是SHA256和SHA512,不过越安全的算法不仅越慢,而且摘要长度更长。
有没有可能两个不同的数据通过某个摘要算法得到了相同的摘要?完全有可能,因为任何摘要算法都是把无限多的数据集合映射到一个有限的集合中。这种情况称为碰撞,比如Bob试图根 据你的摘要反推出一篇文章'how to learn hashlib in python - by Bob'
,并且这篇文章的摘要恰好和你的文章完全一致,这种情况也并非不可能出现,但是非常非常困难。
摘要算法应用
摘要算法能应用到什么地方?举个常用例子:
任何允许用户登录的网站都会存储用户登录的用户名和口令。如何存储用户名和口令呢?方法是存到数据库表中:
name | password |
zhangsan | 123456 |
lisi | abc |
wangwu | abc999 |
如果以明文保存用户口令,如果数据库泄露,所有用户的口令就落入黑客的手里。此外,网站运维人员是可以访问数据库的,也就是能获取到所有用户的口令。
正确的保存口令的方式是不存储用户的明文口令,而是存储用户口令的摘要,比如MD5:
name | password |
zhangsan | e10adc3949ba59abbe56e057f20f883e |
lisi | 900150983cd24fb0d6963f7d28e17f72 |
wangwu | 878ef96e86145580c38c87f0410ad153 |
当用户登录时,首先计算用户输入的明文口令的MD5,然后和数据库存储的MD5对比,如果一致,说明口令输入正确,如果不一致,口令肯定错误。
练习
根据用户输入的登录名和口令模拟实现用户登录的验证
use_hashlib1.py
# -*- coding: utf-8 -*- import hashlib, random #定义生成MD5值函数,接收一个字符串输入返回MD5值 def get_md5(s): return hashlib.md5(s.encode('utf-8')).hexdigest() #定义用户类,加盐用户密码为用户输入的密码加随机生成的20位字符然后再生成MD5 class User(object): def __init__(self, username, password): self.username = username self.salt = ''.join([chr(random.randint(48, 122)) for i in range(20)]) self.password = get_md5(password + self.salt) #模拟生成用户,db为字典key为用户名value为User类实例化后的对象 db = { 'michael': User('michael', '123456'), 'bob': User('bob', 'abc999'), 'alice': User('alice', 'alice2008') } #模拟登陆输入用户名以及密码 #如果输入的用户名加密码对应则返回True #例如参数为('michael','123456')则使用db类存储的密码与重新调用函数get_md5()生成的密码进行对比 #调用函数get_md5()传递的参数为用户输入的密码+存储在db里面随机生成的加盐字符串salt def login(username, password): user = db[username] return user.password == get_md5(password+user.salt) print(login('michael','123456')) print(login('bob','abc999'))