Python-常用模块
一、hashlib模块
①__什么是hash(哈希)?
hash是一类算法,该算法接收传入的内容,经过运算得到一串hash值。
hash值的特点:
- 只要传入的内容一样,得到的hash值必然一样
- 不能由hash值返解出传入的内容
- 不管传入的内容有多大,只要使用的hash算法不变,得到的hash值长度是固定的
②哈希的用途
- 用途一:利用特点2实现密码的密文传输与验证
- 用途二:利用特点1、3实现文件完整性校验
③如何用哈希(import hashlib)
hash算法就像一座工厂,有着不同的加工流水线,流水线接收送进来的原材料(使用update()运送原材料),经过加工返回的产品就是hash值(使用hexdigest()拿到产品)。
import hashlib
m = hashlib.md5() # 选择md5流水线
m.update('hello'.encode('utf-8')) # 使用update()运送原材料,格式选择utf-8
m.update('world'.encode('utf-8')) # 继续运送原材料
res = m.hexdigest() # 一次性取出所有产品,拿到的是'helloworld'的哈希值
print(res) # fc5e038d38a57032085441e7fe7010b0
print(type(res)) # <class 'str'> 得到的值是字符串类型
import hashlib
m = hashlib.sha256() # 选择sha256流水线
m.update('hello'.encode('utf-8'))
m.update('world'.encode('utf-8'))
res = m.hexdigest()
print(res) # 936a185caaa266bb9cbe981e9e05cb78cd732b0b3280eb944412bb6f8f8f07af
针对用途一:
把一段数据分开update多次,与一次update这段数据,得到的结果一样。
import hashlib
m = hashlib.md5()
m.update('hello'.encode('utf-8'))
m.update('world'.encode('utf-8'))
res = m.hexdigest() # 'helloworld'
print(res) # fc5e038d38a57032085441e7fe7010b0
m1 = hashlib.md5('he'.encode('utf-8'))
m1.update('llo'.encode('utf-8'))
m1.update('w'.encode('utf-8'))
m1.update('orld'.encode('utf-8'))
res = m1.hexdigest() # 'helloworld'
print(res) # fc5e038d38a57032085441e7fe7010b0
针对用途二:
我们要实现文件的完整性校验,无非就是把文件扔进哈希工厂里加工,看看得到的哈希值一不一样。
import hashlib
m = hashlib.md5()
m.update(文件所有的内容)
m.hexdigest()
可是这样子做有一个问题,如果文件体积过大,把这个大文件从头至尾都校验一遍是很消耗资源的一件事情,该怎么优化呢?
我们可以随机选定一段内容,放进去校验,这样子就可以在大概率的情况下保证文件是完整的。但说实在的,我们也不能百分之百确定文件的完整性,能做的也只是多来几次随机校验。
二、撞库与密码加盐
撞库
我们现在知道,利用hashlib模块可以对密码进行密文加工和验证。加密算法虽然非常厉害,但有时候依然存在缺陷,即:虽然它自身不支持反解,但我们可以通过撞库实现暴力破解。
import hashlib
m = hashlib.md5()
m.update('admin123'.encode('utf-8'))
res = m.hexdigest()
print(res) # 0192023a7bbd73250516f069df18b500
我们看到,密码admin123的哈希值是0192023a7bbd73250516f069df18b500。
现在如果我们知道密码的哈希值,并且知道密码是由字母adimn与数字123通过未知顺序组成的,我们就可以根据已知信息制作出密码字典。
import hashlib
passwds = [
'admin123',
'123admin',
'a123dmin',
'ad123min',
'ad123min',
'adm123in',
'admi123n',
'省略...'
]
dic = {}
for p in passwds:
res = hashlib.md5(p.encode('utf-8'))
dic[p] = res.hexdigest()
我们有了密码字典,就可以撞库了。(而像这种密码字典,网络上有很多很多)
mima = '0192023a7bbd73250516f069df18b500'
for k, v in dic.items():
if v == mima:
print('撞库成功,明文密码是:%s' % k)
break
撞库成功,明文密码是:admin123
密码加盐(盐:自定义加工)
密码加盐可以增加撞库的成本,就是对密码进行自定义加工之后再进行哈希算法加密。
import hashlib
m = hashlib.md5()
m.update('喜羊羊'.encode('utf-8'))
m.update('admin'.encode('utf-8'))
m.update('灰太狼'.encode('utf-8'))
print(m.hexdigest()) # 2059b347caf000611fec6ec9b0dc9a30
像上面这样,就是对密码先进行了开头结尾的加工之后再进行哈希算法加密。
当然我们也可以这样:得到了一个密码字符串后进行自主分割,再给其中添加自定义的内容后进行合并,进而再使用哈希算法加密。
这样子的话,要想进行撞库破解,就先要知道加盐的内容。加盐的内容知道了后,还需要知道加盐的位置在哪里。
当我们在前台输入密码之后,输入的内容就会先进行处理加盐,再进行哈希算法加密,得到的哈希值与后台保存的密码进行比对,如果一样则登陆成功。