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/