hashlib模块
算法介绍
Python的hashlib提供了常见的摘要算法,如MD5,SHA1等等。
什么是摘要算法呢?摘要算法又称哈希算法、散列算法。它通过一个函数,
把任意长度的数据转换为一个长度固定的数据串(通常用16进制的字符串表示)。
摘要算法就是通过摘要函数f()对任意长度的数据data计算出固定长度的摘要digest,
目的是为了发现原始数据是否被人篡改过。
摘要算法之所以能指出数据是否被篡改过,就是因为摘要函数是一个单向函数,
计算f(data)很容易,但通过digest反推data却非常困难。而且,对原始数据做一个bit的修改,
都会导致计算出的摘要完全不同。
计算出一个字符串的MD5值:
import hashlib
SALT = b'min'
obj = hashlib. md5( SALT)
obj. update( "admin" . encode( "utf-8" ) )
res= obj. hexdigest( )
print ( res)
如果数据量很大,可以分块多次调用update(),最后计算的结果是一样的:
import hashlib
md5= hashlib. md5( )
md5. update( b"hello" )
md5. update( b"yuan" )
print ( md5. hexdigest( ) )
print ( len ( md5. hexdigest( ) ) )
md5= hashlib. md5( )
with open ( "ssh_client.py" , "rb" ) as f:
for line in f:
md5. update( line)
print ( md5. hexdigest( ) )
MD5是最常见的摘要算法,速度很快,生成结果是固定的128 bit字节,通常用一个32位的16进制字符 串表示。
另一种常见的摘要算法是SHA1,调用SHA1和调用MD5完全类似:
import hashlib
sha1 = hashlib. sha1( )
sha1. update( 'how to use sha1 in ' )
sha1. update( 'python hashlib?' )
print sha1. hexdigest( )
SHA1的结果是160 bit字节,通常用一个40位的16进制字符串表示。
比SHA1更安全的算法是SHA256和 SHA512,不过越安全的算法越慢,而且摘要长度更长。
应用
实现一个密文比对登录:
import hashlib
SALT = b'min'
def md5 ( pwd) :
obj = hashlib. md5( SALT)
obj. update( pwd. encode( 'utf-8' ) )
return obj. hexdigest( )
user = input ( "请输入用户名:" )
pwd = input ( "请输入密码:" )
if user == 'min' and md5( pwd) == '88dcb46e24f1085bee9cfe7fdbb6a32b' :
print ( '登录成功' )
else :
print ( '登录失败' )
import random
import hashlib
name = [ "神迷" , "马克" , "禾木" , "影视辑" , "每日" , "影视菌" , "焦点" , "特派" , "星期" ,
"行人" , "探索" , "咖娱" , "mark" , "新片" , "推荐" ]
obj = hashlib. md5( )
result = random. choice( name)
obj. update( result. encode( "utf-8" ) )
true_code = obj. hexdigest( )
print ( true_code)
for el in name:
obj = hashlib. md5( )
obj. update( el. encode( "utf-8" ) )
get_code = obj. hexdigest( )
fin_code = input ( "请复制得到的加密内容:" )
if true_code == fin_code:
print ( "解密后选中的人是:%s" % result)
break
for el in name:
obj = hashlib. md5( )
choice_el = int ( input ( "请输入已选中人序号,并校验:" ) )
obj. update( name[ choice_el] . encode( "utf-8" ) )
check_code = obj. hexdigest( )
if true_code == check_code:
print ( "天选之子是" + ":" + result)
print ( "校验正确,此次有效" )
break
else :
print ( "对应值不对,存在作弊!!!" )
break
任何允许用户登录的网站都会存储用户登录的用户名和口令。如何存储用户名和口令呢?
方法是存到数据库表中:
name | password
- - - - - - - - + - - - - - - - - - -
michael | 123456
bob | abc999
alice | alice2008
如果以明文保存用户口令,如果数据库泄露,所有用户的口令就落入黑客的手里。
此外,网站运维人员是可以访问数据库的,也就是能获取到所有用户的口令。
正确的保存口令的方式是不存储用户的明文口令,而是存储用户口令的摘要,比如MD5:
username | password
- - - - - - - - - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
michael | e10adc3949ba59abbe56e057f20f883e
bob | 878ef96e86145580c38c87f0410ad153
alice | 99b1c2188db85afee403b1536010c2c9
这样,无需破解,只需要对比数据库的MD5,黑客就获得了使用常用口令的用户账号。
对于用户来讲,当然不要使用过于简单的口令。但是,我们能否在程序设计上对简单口令加强保护呢?
由于常用口令的MD5值很容易被计算出来,所以,要确保存储的用户口令不是那些已经被计算出来的常
用口令的MD5,这一方法通过对原始口令加一个复杂字符串来实现,俗称“加盐”:
hashlib. md5( "salt" . encode( "utf8" ) )
经过Salt处理的MD5口令,只要Salt不被黑客知道,即使用户输入简单口令,
也很难通过MD5反推明文口令。
但是如果有两个用户都使用了相同的简单口令比如123456,在数据库中,将存储两条相同的MD5值,
这说明这两个用户的口令是一样的。有没有办法让使用相同口令的用户存储不同的MD5呢?
如果假定用户无法修改登录名,就可以通过把登录名作为Salt的一部分来计算MD5,
从而实现相同口令的用户也存储不同的MD5。
摘要算法在很多地方都有广泛的应用。要注意摘要算法不是加密算法,不能用于加密(因为无法通过摘要反推明文);
只能用于防篡改,但是它的单向计算特性决定了可以在不存储明文口令的情况下验证用户口令。
configparser模块
该模块适用于配置文件的格式与windows ini文件类似,可以包含一个或多个节(section),
每个节可以有多个参数(键=值)。
创建文件
来看一个好多软件的常见文档格式如下:
[ 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)
查找文件
import configparser
config = configparser. ConfigParser( )
print ( config. sections( ) )
config. read( 'example.ini' )
print ( config. sections( ) )
print ( 'bytebong.com' in config)
print ( 'bitbucket.org' in config)
print ( config[ 'bitbucket.org' ] [ "user" ] )
print ( config[ 'DEFAULT' ] [ 'Compression' ] )
print ( config[ 'topsecret.server.com' ] [ 'ForwardX11' ] )
print ( config[ 'bitbucket.org' ] )
for key in config[ 'bitbucket.org' ] :
print ( key)
print ( config. options( 'bitbucket.org' ) )
print ( config. items( 'bitbucket.org' ) )
print ( config. get( 'bitbucket.org' , 'compression' ) )
增删改操作
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" )
config. set ( 'topsecret.server.com' , 'k1' , '11111' )
config. set ( 'yuan' , 'k2' , '22222' )
config. write( open ( 'new2.ini' , "w" ) )
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
logger = logging. basicConfig(
filename= 'xxxxxxx.txt' ,
format = '%(asctime)s - %(name)s - %(levelname)s -%(module)s: %(message)s' ,
datefmt= '%Y-%m-%d %H:%M:%S' ,
level= 30 )
logging. debug( 'x1' )
logging. info( 'x2' )
logging. warning( 'x3' )
logging. error( 'x4' )
logging. critical( 'x5' )
logging. log( 100 , 'x6' )
import traceback
def func ( ) :
try :
a = a + 1
except Exception as e:
msg = traceback. format_exc( )
logging. error( msg)
import logging
logger1 = logging. basicConfig( filename= 'x1.txt' ,
format = '%(asctime)s - %(name)s - %(levelname)s -%(module)s: %(message)s' ,
datefmt= '%Y-%m-%d %H:%M:%S' ,
level= 30 )
logger = logging. basicConfig( filename= 'x2.txt' ,
format = '%(asctime)s - %(name)s - %(levelname)s -%(module)s: %(message)s' ,
datefmt= '%Y-%m-%d %H:%M:%S' ,
level= 30 )
logging. error( 'x4' )
logging. error( 'x5' )
日志切割:
import time
import logging
from logging import handlers
sh = logging. StreamHandler( )
rh = handlers. RotatingFileHandler( 'myapp.log' , maxBytes= 1024 , backupCount= 5 )
fh = handlers. TimedRotatingFileHandler( filename= 'x2.log' , when= 's' , interval= 5 , encoding= 'utf-8' )
logging. basicConfig(
format = '%(asctime)s - %(name)s - %(levelname)s -%(module)s: %(message)s' ,
datefmt= '%Y-%m-%d %H:%M:%S %p' ,
handlers= [ fh, sh, rh] ,
level= logging. ERROR
)
for i in range ( 1 , 100000 ) :
time. sleep( 1 )
logging. error( 'KeyboardInterrupt error %s' % str ( i) )
配置参数:
logging. basicConfig( ) 函数中可通过具体参数来更改logging模块默认行为,可用参数有:
filename:用指定的文件名创建FiledHandler,这样日志会被存储在指定的文件中。
filemode:文件打开方式,在指定了filename时使用这个参数,默认值为“a”还可指定为“w”。
format :指定handler使用的日志显示格式。
datefmt:指定日期时间格式。
level:设置rootlogger(后边会讲解具体概念)的日志级别
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( )
fh = logging. FileHandler( 'test.log' , encoding= 'utf-8' )
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. 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' )
自定义日志
import logging
file_handler = logging. FileHandler( 'l1.log' , 'a' , encoding= 'utf-8' )
file_handler. setFormatter( logging. Formatter
( fmt= "%(asctime)s - %(name)s - %(levelname)s -%(module)s: %(message)s" )
)
logger1 = logging. Logger( 's1' , level= logging. ERROR)
logger1. addHandler( file_handler)
logger1. error( '123123123' )
file_handler2 = logging. FileHandler( 'l2.log' , 'a' , encoding= 'utf-8' )
file_handler2. setFormatter( logging. Formatter
( fmt= "%(asctime)s - %(name)s - %(levelname)s -%(module)s: %(message)s" )
)
logger2 = logging. Logger( 's2' , level= logging. ERROR)
logger2. addHandler( file_handler2)
logger2. error( '666' )
示例用法 用上上面已经创建好的日志文件