1. 程式人生 > >鐵樂學python26_hashlib+configparser+logging模塊

鐵樂學python26_hashlib+configparser+logging模塊

程序設計 fun 開發 logs 喜歡 很快 方法 break igp

大部份內容摘自博客http://www.cnblogs.com/Eva-J/

hashlib模塊算法介紹

Python的hashlib提供了常見的摘要算法,如MD5,SHA1等等。
什麽是摘要算法呢?
摘要算法又稱哈希算法、散列算法。
它通過一個函數,把任意長度的數據轉換為一個長度固定的數據串(通常用16進制的字符串表示)。
摘要算法就是通過摘要函數f()對任意長度的數據data計算出固定長度的摘要digest,
目的是為了發現原始數據是否被人篡改過。
摘要算法之所以能指出數據是否被篡改過,就是因為摘要函數是一個單向函數,
計算f(data)很容易,但通過digest反推data卻非常困難。
而且,對原始數據做一個bit的修改,都會導致計算出的摘要完全不同。
我們以常見的摘要算法MD5為例,

計算出一個字符串的MD5值:
import hashlib
md5 = hashlib.md5()
md5.update(b‘how to use md5 in python hashlib?‘) #註,要轉換成b字節模式才能正常
print(md5.hexdigest()) # d26a53750bc40b38b65a520292f69306

如果數據量很大,可以分塊多次調用update(),
一段字符串直接進行摘要和分成幾段摘要的結果是相同的:

md5 = hashlib.md5()
md5.update(b‘how to use md5 in ‘)
md5.update(b‘python hashlib?‘
) print(md5.hexdigest()) # d26a53750bc40b38b65a520292f69306 模仿文件校驗md5一致 def check_md5(filename): md5 = hashlib.md5() with open(filename, ‘rb‘) as f: while True: content = f.read(4096) # 分段讀取,防止占用大段內存 if content: md5.update(content) else
: break return md5.hexdigest() file1 = check_md5(‘md5-test1‘) file2 = check_md5(‘md5-test2‘) print(file1) # 2e5f9458bcd27e3c2b5908af0b91551a print(file2) # 2e5f9458bcd27e3c2b5908af0b91551a md5摘要加密傳輸進來的明文密碼,且動態加鹽(更安全) import hashlib # md5摘要加密傳輸進來的明文密碼 def md5_digest(user, plain_pass): md5 = hashlib.md5(user[::-1].encode(‘utf-8‘)) # 創建了一個md5算法的對象,且對定義的鹽切片倒序 md5.update(plain_pass.encode(‘utf-8‘)) return md5.hexdigest() user = ‘bilibili‘ pwd = ‘123456‘ print(md5_digest(user, pwd)) # b442d27216d7e1dd54d6419a9d31056f

MD5是最常見的摘要算法,速度很快,生成結果是固定的128 bit字節,通常用一個32位的16進制字符串表示。
另一種常見的摘要算法是SHA1,調用SHA1和調用MD5完全類似,就是將前面的md5改成sha1而己。
SHA1的結果是160 bit字節,通常用一個40位的16進制字符串表示。
比SHA1更安全的算法是SHA256和SHA512,不過越安全的算法越慢,而且摘要長度更長。

摘要算法應用

任何允許用戶登錄的網站都會存儲用戶登錄的用戶名和口令。
如何存儲用戶名和口令呢?方法是存到數據庫表中:

name    | password
--------+----------
michael | 123456
bob     | abc999
alice   | alice2008
如果以明文保存用戶口令,如果數據庫泄露,所有用戶的口令就落入黑客的手裏。
此外,網站運維人員是可以訪問數據庫的,也就是能獲取到所有用戶的口令。
正確的保存口令的方式是不存儲用戶的明文口令,而是存儲用戶口令的摘要,比如MD5:
username | password
---------+---------------------------------
michael  | e10adc3949ba59abbe56e057f20f883e
bob      | 878ef96e86145580c38c87f0410ad153
alice    | 99b1c2188db85afee403b1536010c2c9

考慮這麽個情況,很多用戶喜歡用123456888888,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。

摘要算法在很多地方都有廣泛的應用。
要註意摘要算法不是加密算法,不能用於加密(因為無法通過摘要反推明文),只能用於防篡改,
但是它的單向計算特性決定了可以在不存儲明文口令的情況下驗證用戶口令。

configparser模塊

開發流程:一般開發完成後先給予測試,測試通過後再給實施或運維部門部署到線上。
需要配置文件以便運維部門或開源項目的話給到用戶自定義一些符合實際的參數,而不需要到代碼文件裏修改。

該模塊適用於配置文件的格式與windows ini文件類似,
可以包含一個或多個節(section),每個節可以有多個option參數(鍵=值)。

創建配置文件

常見文檔格式如下:

[DEFAULT]
ServerAliveInterval = 45
Compression = yes
CompressionLevel = 9
ForwardX11 = yes

[bitbucket.org]
User = hg

[topsecret.server.com]
Port = 50022
ForwardX11 = no

用python生成一個配置文件:
import configparser
config = configparser.ConfigParser()
config["DEFAULT"] = {‘ServerAliveInterval‘: ‘45‘,
                     ‘Compression‘: ‘yes‘,
                     ‘CompressionLevel‘: ‘9‘,
                     ‘ForwardX11‘:‘yes‘
                     }
config[‘bitbucket.org‘] = {‘User‘:‘hg‘}
config[‘topsecret.server.com‘] = {‘Host Port‘:‘50022‘,‘ForwardX11‘:‘no‘}
with open(‘example.ini‘, ‘w‘) as configfile:
config.write(configfile)

生成的example.ini如下,註意裏面的選項會變成小寫(不區分大小寫):
[DEFAULT]
compressionlevel = 9
serveraliveinterval = 45
forwardx11 = yes
compression = yes

[bitbucket.org]
user = hg

[topsecret.server.com]
host port = 50022
forwardx11 = no

查看配置文件
由上至下依次敲入執行來看效果
import configparser
config = configparser.ConfigParser()
#---------------------------查找文件內容,基於字典的形式
print(config.sections())        #  顯示[] ,默認,因這個時候還不知道要讀取哪個配置文件的內容
config.read(‘example.ini‘)      #  讀取前面創建好的那個配置文件示例內容
print(config.sections())        #  這回顯示出[‘bitbucket.org‘, ‘topsecret.server.com‘]
print(‘bytebong.com‘ in config) #  判斷有無此參數在配置文件中 False
print(‘bitbucket.org‘ in config) # 判斷有無此參數在配置文件中 True
print(config[‘bitbucket.org‘]["user"])  # 類似字典通過key找值 hg
print(config[‘DEFAULT‘][‘Compression‘]) # yes
print(config[‘topsecret.server.com‘][‘ForwardX11‘])  # no
print(config[‘bitbucket.org‘])          # 查看對象 <Section: bitbucket.org>

for key in config[‘bitbucket.org‘]:     # 註意,有default會默認default的鍵
    print(key)
# user
# compressionlevel
# serveraliveinterval
# forwardx11
# compression

print(config.options(‘bitbucket.org‘))  # 同for循環,找到‘bitbucket.org‘下所有鍵
print(config.items(‘bitbucket.org‘))    # 找到‘bitbucket.org‘下所有鍵值對
# [(‘compressionlevel‘, ‘9‘), (‘serveraliveinterval‘, ‘45‘), (‘forwardx11‘, ‘yes‘), (‘compression‘, ‘yes‘), (‘user‘, ‘hg‘)]
print(config.get(‘bitbucket.org‘,‘compression‘)) # yes       get方法Section下的key對應的value

增刪改配置文件:
import configparser
config = configparser.ConfigParser()
config.read(‘example.ini‘)
config.add_section(‘yuan‘)
# 註,此時還只是在內存中讀取出配置文件內容,
# 並在內存中添加了,還需執行最下方的寫入句柄操作才能生效,下面的操作同理
config.remove_section(‘bitbucket.org‘)
config.remove_option(‘topsecret.server.com‘, "forwardx11")
# remove 移除
config.set(‘topsecret.server.com‘, ‘k1‘, ‘11111‘)
config.set(‘yuan‘, ‘k2‘, ‘22222‘)
# set 增加或修改
config.write(open(‘example.ini‘, "w"))

執行完後,原配置文件被改成了如下模樣:
[DEFAULT]
compressionlevel = 9
serveraliveinterval = 45
forwardx11 = yes
compression = yes

[topsecret.server.com]
host port = 50022
k1 = 11111

[yuan]
k2 = 22222

logging模塊
函數式簡單配置
import logging  
logging.debug(‘debug message‘)  
logging.info(‘info message‘)  
logging.warning(‘warning message‘)  
logging.error(‘error message‘)  
logging.critical(‘critical message‘) 

默認情況下Python的logging模塊將日誌打印到了標準輸出中,
且只顯示了大於等於WARNING級別的日誌,
這說明默認的日誌級別設置為WARNING(日誌級別等級CRITICAL > ERROR > WARNING > INFO > DEBUG),
默認的日誌格式為日誌級別:Logger名稱:用戶輸出消息。

靈活配置日誌級別,日誌格式,輸出位置:
import logging  
logging.basicConfig(level=logging.DEBUG,  
                    format=%(asctime)s %(filename)s[line:%(lineno)d] %(levelname)s %(message)s,  
                    datefmt=‘%a, %d %b %Y %H:%M:%S‘,  
                    filename=‘test.log‘,  
                    filemode=‘w‘)  
  
logging.debug(‘debug message‘)  
logging.info(‘info message‘)  
logging.warning(‘warning message‘)  
logging.error(‘error message‘)  
logging.critical(‘critical message‘)

查看test.log:
Mon, 23 Apr 2018 20:45:04 hashlib_test.py[line:205] DEBUG debug message
Mon, 23 Apr 2018 20:45:04 hashlib_test.py[line:206] INFO info message
Mon, 23 Apr 2018 20:45:04 hashlib_test.py[line:207] WARNING warning message
Mon, 23 Apr 2018 20:45:04 hashlib_test.py[line:208] ERROR error message
Mon, 23 Apr 2018 20:45:04 hashlib_test.py[line:209] CRITICAL critical message

logging.basicConfig()函數中可通過具體參數來更改logging模塊默認行為,可用參數有:

filename:用指定的文件名創建FiledHandler,這樣日誌會被存儲在指定的文件中。
filemode:文件打開方式,在指定了filename時使用這個參數,默認值為“a”(追加模式)還可指定為“w”。
format:指定handler使用的日誌顯示格式。
datefmt:指定日期時間格式,設置asctime中的時間顯示格式。
level:設置rootlogger的日誌級別,可選DEBUG調試/INFO正常/WARNING警告/ERROR錯誤/CRITICAL嚴重錯誤 等級別。
stream:用指定的stream創建StreamHandler。可以指定輸出到sys.stderr,sys.stdout或者文件(f=open(‘test.log’,’w’)),默認為sys.stderr。若同時列出了filename和stream兩個參數,則stream參數會被忽略。

format參數中可能用到的格式化串:
%(name)s Logger的名字
%(levelno)s 數字形式的日誌級別
%(levelname)s 文本形式的日誌級別
%(pathname)s 調用日誌輸出函數的模塊的完整路徑名,可能沒有
%(filename)s 調用日誌輸出函數的模塊的文件名
%(module)s 調用日誌輸出函數的模塊名
%(funcName)s 調用日誌輸出函數的函數名
%(lineno)d 調用日誌輸出函數的語句所在的代碼行
%(created)f 當前時間,用UNIX標準的表示時間的浮 點數表示
%(relativeCreated)d 輸出日誌信息時的,自Logger創建以 來的毫秒數
%(asctime)s 字符串形式的當前時間。默認格式是 “2003-07-08 16:49:45,896”。逗號後面的是毫秒
%(thread)d 線程ID。可能沒有
%(threadName)s 線程名。可能沒有
%(process)d 進程ID。可能沒有
%(message)s用戶輸出的消息

logger對象配置

import logging

logger = logging.getLogger()

# 創建一個handler,用於寫入日誌文件
fh = logging.FileHandler(‘test.log‘,encoding=‘utf-8‘) 

# 再創建一個handler,用於輸出到控制臺 
ch = logging.StreamHandler() 

formatter = logging.Formatter(%(asctime)s - %(name)s - %(levelname)s - %(message)s)

fh.setLevel(logging.DEBUG)

fh.setFormatter(formatter) 
ch.setFormatter(formatter) 

logger.addHandler(fh) #logger對象可以添加多個fh和ch對象 
logger.addHandler(ch)

logger.debug(‘logger debug message‘) 
logger.info(‘logger info message‘) 
logger.warning(‘logger warning message‘) 
logger.error(‘logger error message‘) 
logger.critical(‘logger critical message‘)

logging庫提供了多個組件:Logger、Handler、Filter、Formatter。
Logger對象提供應用程序可直接使用的接口,
Handler發送日誌到適當的目的地,
Filter提供了過濾日誌信息的方法,
Formatter指定日誌顯示格式。
另外,可以通過:logger.setLevel(logging.Debug)設置級別,
當然,也可以通過fh.setLevel(logging.Debug)單對文件流設置某個級別。

end
2018-4-23

鐵樂學python26_hashlib+configparser+logging模塊