Python的hashlib提供了常见的摘要算法,如MD5,SHA1等等。python
什么是摘要算法呢?摘要算法又称哈希算法、散列算法。它经过一个函数,把任意长度的数据转换为一个长度固定的数据串(一般用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 s = 'tz_spider' m = hashlib.md5() # 加密数据都是bytes
m.update(s.encode('utf-8')) print('md5 hash %s'%m.hexdigest()) """ md5 hash a4499790ea68682695a0a168a8ec1ecc """
若是数据量很大,能够分块屡次调用update()
,最后计算的结果是同样的:ide
import hashlib md5 = hashlib.md5() md5.update('人生苦短,'.encode('utf-8')) md5.update('我学Python'.encode('utf-8')) print(md5.hexdigest()) import hashlib md5 = hashlib.md5() md5.update('人生苦短,我学Python'.encode('utf-8')) print(md5.hexdigest()) """ d51a987403720a379fa5d20ab8b7741c d51a987403720a379fa5d20ab8b7741c """
MD5是最多见的摘要算法,速度很快,生成结果是固定的128 bit字节,一般用一个32位的16进制字符串表示。函数
另外一种常见的摘要算法是SHA1,调用SHA1和调用MD5彻底相似:编码
import hashlib md5 = hashlib.sha1() md5.update('人生苦短,'.encode('utf-8')) md5.update('我学Python'.encode('utf-8')) print(md5.hexdigest()) import hashlib md5 = hashlib.sha1() md5.update('人生苦短,我学Python'.encode('utf-8')) print(md5.hexdigest()) """ 5723b4cd6bc67f1f3682cab2a382e333518ed23a 5723b4cd6bc67f1f3682cab2a382e333518ed23a """
SHA1的结果是160 bit字节,一般用一个40位的16进制字符串表示。
摘要算法主要用于用户登陆时,对口令进行MD5加密,存储到数据库中,存储MD5的好处是即便运维人员能访问数据库,也没法获知用户的明文口令。
def get_md5(s): md5 = hashlib.md5() md5.update(s.encode('utf-8')) return md5.hexdigest() user_md5_dict = {} user_dict = { 'michael': '123456', 'bob': 'abc'} for item in user_dict: user_md5_dict[item] = get_md5(user_dict.get(item)) print(user_md5_dict) """ {'michael': 'e10adc3949ba59abbe56e057f20f883e', 'bob': '900150983cd24fb0d6963f7d28e17f72'} """
采用MD5存储口令是否就必定安全呢?也不必定,不少用户喜欢用123456
,888888
,password
这些简单的口令,因而,黑客能够事先计算出这些经常使用口令的MD5值,获得一个反推表:
'e10adc3949ba59abbe56e057f20f883e': '123456' '21218cca77804d2ba1922c33e0151105': '888888'
这样,无需破解,只须要对比数据库的MD5,黑客就得到了使用经常使用口令的用户帐号(撞库)。
因为经常使用口令的MD5值很容易被计算出来,因此,要确保存储的用户口令不是那些已经被计算出来的经常使用口令的MD5,这一方法经过对原始口令加一个复杂字符串来实现,俗称“加盐”:
def calc_md5(password, salt='add salt'): md5 = hashlib.md5() md5.update((password+salt).encode('utf-8')) a = md5.hexdigest() return a user_md5_dict = {} user_dict = { 'michael': '123456', 'bob': 'abc'} for item in user_dict: user_md5_dict[item] = calc_md5(user_dict.get(item)) print(user_md5_dict) """ {'michael': '121e5a2806adb57b7f5ddfb49c58cb38', 'bob': '655cb18b65100d50c375826e4a7138d9'} """
通过Salt处理的MD5口令,只要Salt不被黑客知道,即便用户输入简单口令,也很难经过MD5反推明文口令。
可是若是有两个用户都使用了相同的简单口令好比123456
,在数据库中,将存储两条相同的MD5值,这说明这两个用户的口令是同样的。有没有办法让使用相同口令的用户存储不一样的MD5呢?
若是假定用户没法修改登陆名,就能够经过把登陆名做为Salt的一部分来计算MD5,从而实现相同口令的用户也存储不一样的MD5。
def calc_md5(user, password, salt='add salt'): md5 = hashlib.md5() md5.update((user + password + salt).encode('utf-8')) a = md5.hexdigest() return a user_md5_dict = {} user_dict = { 'michael': '123456', 'bob': '123456'} for item in user_dict: user_md5_dict[item] = calc_md5(item, user_dict.get(item)) print(user_md5_dict) """ {'michael': '18833a2efa41021c1659af9eb7ffc0e5', 'bob': 'b2c512421985a2622a64fa84b486dc0b'} """
获取文件的MD5
import os def calc_md5(filename): """ 用于获取文件的md5值 :param filename: 文件名 :return: MD5码 """
if not os.path.isfile(filename): # 若是校验md5的文件不是文件,返回空
return myhash = hashlib.md5() f = open(filename, 'rb') while True: b = f.read(2048) if not b: break myhash.update(b) f.close() return myhash.hexdigest() print(calc_md5('BaiduStockInfo.txt')) """ 94da595be98b4c65fc1ccf697a435322 """
base64模块是用来做base64编码解码的。这种编码方式在电子邮件中是很常见的。
它能够把不能做为文本显示的二进制数据编码为可显示的文本信息。编码后的文本大小会增大1/3。
Base64的原理很简单,首先,准备一个包含64个字符的数组:
['A', 'B', 'C', ... 'a', 'b', 'c', ... '0', '1', ... '+', '/']
而后,对二进制数据进行处理,每3个字节一组,一共是3x8=24
bit,划为4组,每组正好6个bit
这样咱们获得4个数字做为索引,而后查表,得到相应的4个字符,就是编码后的字符串。
因此,Base64编码会把3字节的二进制数据编码为4字节的文本数据,长度增长33%,好处是编码后的文本数据能够在邮件正文、网页等直接显示。
若是要编码的二进制数据不是3的倍数,最后会剩下1个或2个字节怎么办?Base64用\x00
字节在末尾补足后,再在编码的末尾加上1个或2个=
号,表示补了多少字节,解码的时候,会自动去掉。
import base64 s = b'1234567' s1 = base64.b64encode(s) # 编码
print(s1) print(base64.b64decode(s1)) # 解码
输出
b'MTIzNDU2Nw==' b'1234567'
因为标准的Base64编码后可能出现字符+
和/
,在URL中就不能直接做为参数,因此又有一种"url safe"的base64编码,其实就是把字符+
和/
分别变成-
和_
import base64 s = b'i\xb7\x1d\xfb\xef\xff' s1 = base64.b64encode(s) # 编码 print(s1) s2 = base64.urlsafe_b64encode(s) print(s2) print(base64.b64decode(s1)) # 解码 print(base64.urlsafe_b64decode(s2)) # 解码
输出
b'abcd++//' b'abcd--__' b'i\xb7\x1d\xfb\xef\xff' b'i\xb7\x1d\xfb\xef\xff'
参考https://www.liaoxuefeng.com/