hashlib模块の拾遗

1、加密算法介绍

1.1 HASH

Hash,一般翻译做“散列”,也有直接音译为”哈希”的,就是把任意长度的输入(又叫做预映射,pre-image),通过散列算法,变换成固定长度的输出,该输出就是散列值,也称摘要值,该算法就是哈希函数,也称摘要函数。这种转换是一种压缩映射,也就是,散列值的空间通常远小于输入的空间,不同的输入可能会散列成相同的输出,而不可能从散列值来唯一的确定输入值。

简单的说就是一种将任意长度的消息压缩到某一固定长度的消息摘要的函数。

HASH主要用于信息安全领域中加密算法,他把一些不同长度的信息转化成杂乱的128位的编码里,叫做HASH值.也可以说,hash就是找到一种数据内容和数据存放地址之间的映射关系

1.2 MD5

摘要函数是一个单向函数,通过摘要函数对任意长度的数据计算出固定长度的摘要数列,目的是为了提供一个验证文件未被篡改的方法。

1.2.1 什么是MD5算法

MD5讯息摘要演算法(英语:MD5 Message-Digest Algorithm),一种被广泛使用的密码杂凑函数,可以产生出一个128位的散列值(hash value),用于确保信息传输完整一致。MD5的前身有MD2、MD3和MD4。

1.2.2 MD5功能

  • 输入任意长度的信息,经过处理,输出为128位的信息(数字指纹)

  • 不同的输入得到的不同的结果(唯一性)

1.2.3 MD5算法的特点

  • 压缩性:任意长度的数据,算出的MD5值的长度都是固定的

  • 容易计算:从原数据计算出MD5值很容易

  • 抗修改性:对原数据进行任何改动,修改一个字节生成的MD5值区别也会很大

  • 强抗碰撞:已知原数据和MD5,想找到一个具有相同MD5值的数据(即伪造数据)是非常困难的。

1.2.4 MD5算法是否可逆?

MD5不可逆的原因是其是一种散列函数,使用的是hash算法,在计算过程中原文的部分信息是丢失了的。

1.2.5 MD5用途

  • 防止被篡改:

比如发送一个电子文档,发送前,我先得到MD5的输出结果a。然后在对方收到电子文档后,对方也得到一个MD5的输出结果b。如果a与b一样就代表中途未被篡改。

比如我提供文件下载,为了防止不法分子在安装程序中添加木马,我可以在网站上公布由安装文件得到的MD5输出结果。

SVN在检测文件是否在CheckOut后被修改过,也是用到了MD5.

  • 防止直接看到明文

现在很多网站在数据库存储用户的密码的时候都是存储用户密码的MD5值。这样就算不法分子得到数据库的用户密码的MD5值,也无法知道用户的密码。(比如在UNIX系统中用户的密码就是以MD5(或其它类似的算法)经加密后存储在文件系统中。当用户登录的时候,系统把用户输入的密码计算成MD5值,然后再去和保存在文件系统中的MD5值进行比较,进而确定输入的密码是否正确。通过这样的步骤,系统在并不知道用户密码的明码的情况下就可以确定用户登录系统的合法性。这不但可以避免用户的密码被具有系统管理员权限的用户知道,而且还在一定程度上增加了密码被破解的难度。)

  • 防止抵赖(数字签名):

这需要一个第三方认证机构。例如A写了一个文件,认证机构对此文件用MD5算法产生摘要信息并做好记录。若以后A说这文件不是他写的,权威机构只需对此文件重新产生摘要信息,然后跟记录在册的摘要信息进行比对,相同的话,就证明是A写的了。这就是所谓的“数字签名”。

1.3 SHA-1

安全哈希算法(Secure Hash Algorithm)主要适用于数字签名标准(Digital Signature Standard DSS)里面定义的数字签名算法(Digital Signature Algorithm DSA)。对于长度小于2^64位的消息,SHA1会产生一个160位的消息摘要。当接收到消息的时候,这个消息摘要可以用来验证数据的完整性。

SHA是美国国家安全局设计的,由美国国家标准和技术研究院发布的一系列密码散列函数。

由于MD5和SHA-1于2005年被山东大学的教授王小云破解了,科学家们又推出了SHA224, SHA256, SHA384, SHA512,当然位数越长,破解难度越大,但同时生成加密的消息摘要所耗时间也更长。目前最流行的是加密算法是SHA-256 .

1.4 MD5与SHA-1的比较

由于MD5与SHA-1均是从MD4发展而来,它们的结构和强度等特性有很多相似之处,SHA-1与MD5的最大区别在于其摘要比MD5摘要长32 比特。对于强行攻击,产生任何一个报文使之摘要等于给定报文摘要的难度:MD5是2128数量级的操作,SHA-1是2160数量级的操作。产生具有相同摘要的两个报文的难度:MD5是264是数量级的操作,SHA-1 是280数量级的操作。因而,SHA-1对强行攻击的强度更大。但由于SHA-1的循环步骤比MD5多80:64且要处理的缓存大160比特:128比特,SHA-1的运行速度比MD5慢。

2、hashlib模块

Python的 hashlib 提供了常见的摘要算法,用于加密相关的操作,3.x里用hashlib代替了md5模块和sha模块,主要提供 SHA1, SHA224, SHA256, SHA384, SHA512 ,MD5 等算法。

什么是摘要算法呢?摘要算法又称哈希算法、散列算法。它通过一个函数,把任意长度的数据转换为一个长度固定的数据串(通常用16进制的字符串表示)。

摘要算法就是通过摘要函数f()对任意长度的数据data计算出固定长度的摘要digest,目的是为了发现原始数据是否被人篡改过。

摘要算法之所以能指出数据是否被篡改过,就是因为摘要函数是一个单向函数,计算f(data)很容易,但通过digest反推data却非常困难。而且,对原始数据做一个bit的修改,都会导致计算出的摘要完全不同。

要为一个数据块(这里是一个 unicode 字符串转化成对应的字节串)计算 MD5 哈希值或者 摘要, 首先要创建哈希对象, 然后为这个对象添加数据并且进行 digest() 或者 hexdigest() 调用。

2.1 查看平台支持的哈希算法

具体某一种哈希算法的支持与否取决于操作系统,因为有些哈希算法依赖特定的底层驱动库。

  • 查看全平台支持的哈希算法可以使用 algorithms_guaranteed
  • 查看当前使用平台所支持的哈希算法可使用 algorithms_available
In [20]: import hashlib

In [21]: print('Guaranteed:\n{}\n'.format( ', '.join(sorted(hashlib.algorithms_guaranteed))))
Guaranteed:
blake2b, blake2s, md5, sha1, sha224, sha256, sha384, sha3_224, sha3_256, sha3_384, sha3_512, sha512, shake_128, shake_256


In [22]: print('Available:\n{}'.format( ', '.join(sorted(hashlib.algorithms_available))))
Available:
DSA, DSA-SHA, MD4, MD5, RIPEMD160, SHA, SHA1, SHA224, SHA256, SHA384, SHA512, blake2b, blake2s, dsaEncryption, dsaWithSHA, ecdsa-with-SHA1, md4, md5, ripemd160, sha, sha1, sha224, sha256, sha384, sha3_224, sha3_256, sha3_384, sha3_512, sha512, shake_128, shake_256, whirlpool

In [23]:         

2.2 md5 安全吗 ?

答案是否定的,存在一定的高风险。毕竟 md5 库已经被破解了,普通的字符串md5值已被录入,并且保存数据库,甚至可以直接用md5值去网站破解原字符串,故谓之md5学习练习入门即可。

update()方法现在只接受bytes类型的数据,不接收str类型,因为散列仅对字节起作用,而不对字符起作用。

import hashlib
md5 = hashlib.md5()       # 创建一个哈希对象,通过构造函数得到一个hash对象
md5.update(b'123456')     # update的参数是一个byte类型,不接受str类型,通过update方法添加消息
ret = md5.hexdigest()     # hex  digest  返回16进制格式的hash值,即16进制str类型的消息摘要
print(ret)

输出一个md5值,这个 字符串对应的 md5 值

e10adc3949ba59abbe56e057f20f883e

然后呢,你在 https://pmd5.com 可以直接破解, 对,就是这样
在这里插入图片描述
使用了md5也不安全的几个场景

# 撞库 :
# 123456 e10adc3949ba59abbe56e057f20f883e
# f51703256a38e6bab3d9410a070c32ea
# 用户的恶意注册
# 只要是一成不变的算法,那么我们总能得到相同的结果

2.3 md5 动态加盐

由于常用口令的 MD5值 很容易被计算出来,所以,要确保存储的用户口令不是那些已经被计算出来的常用口令的 MD5,这一方法通过对原始口令加一个复杂字符串来实现,俗称“加盐”:

hashlib.md5("salt".encode("utf8"))

经过Salt处理的MD5口令,只要Salt不被黑客知道,即使用户输入简单口令,也很难通过MD5反推明文口令。

但是如果有两个用户都使用了相同的简单口令比如123456,在数据库中,将存储两条相同的MD5值,这说明这两个用户的口令是一样的。有没有办法让使用相同口令的用户存储不同的MD5呢?

如果假定用户无法修改登录名,就可以通过把登录名作为Salt的一部分来计算MD5,从而实现相同口令的用户也存储不同的MD5。

摘要算法在很多地方都有广泛的应用。要注意摘要算法不是加密算法,不能用于加密(因为无法通过摘要反推明文),只能用于防篡改,但是它的单向计算特性决定了可以在不存储明文口令的情况下验证用户口令。

# 动态加盐:盐会变化,会随着用户的变化而变化,对相同的用户,每次的变化都必须相同
import hashlib
m = hashlib.md5(b'salt29835798370740239480707508893')  # 句柄  盐是salt29835798370740239480707508893jiu
m.update(b'123456')  # update的参数是一个bytes类型
ret = m.hexdigest()  # hex digest
rint(ret)

输出:

18e934defdbda3071c5bcb4069603b15
import hashlib
# md5
m = hashlib.md5()
m.update(b"Hello")
m.update(b"It's me")
print(m.digest())  # 返回2进制格式的hash值
m.update(b"It's been a long time since last time we ...")
print(m.hexdigest()) # 返回16进制格式的hash值

2.4 文件校验,当数据量很大时

如果数据量很大,可以分块多次调用update(),最后计算的结果是一样的:

import hashlib
md5 = hashlib.md5(b'666pythonHashlibTestSalt+00700?')
md5.update(b'how to use md5 salt in python hashlib?')
print(md5.hexdigest())
# 5b8cc9755e51b32e87645184c12e4d3d

可以看到,结果是一样的,前提是同一个句柄作用下

import hashlib
md5 = hashlib.md5(b'666pythonHashlibTestSalt+00700?')
md5.update(b'how to use md5 ')
md5.update(b'salt in python hashlib?')
print(md5.hexdigest())
# 5b8cc9755e51b32e87645184c12e4d3d

MD5是最常见的摘要算法,速度很快,生成结果是固定的128 bit字节,通常用一个32位的16进制字符串表示。另一种常见的摘要算法是SHA1,调用SHA1和调用MD5完全类似:

import hashlib
sha1 = hashlib.sha1(b'666666pythonHashlibTestSalt+00700?')
sha1.update(b"how to use sha1 in ")
sha1.update(b"python hashlib")
print(sha1.hexdigest())

输出:

0cefda9e0012c6a04ab400269c945efe52a0c7a7

SHA1的结果是160 bit字节,通常用一个40位的16进制字符串表示。比SHA1更安全的算法是SHA256和SHA512,不过越安全的算法越慢,而且摘要长度更长。

2.5 摘要算法的应用

目前来看,各网站需要记录操作的就是得实现用户登录吧,那就得数据库保存用户名及其密码,但你直接明文存储的话,你老板也许会抽死你~

拔刀吧,各位,这里用到hashlib,再加点盐,搞成密文不就行啦!

username | password
---------+---------------------------------
michael  | e10adc3949ba59abbe56e057f20f883e
bob      | 878ef96e86145580c38c87f0410ad153
alice    | 99b1c2188db85afee403b1536010c2c9

啥玩意??为啥要加盐?

考虑这么个情况,很多用户喜欢用123456,888888,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。

摘要算法在很多地方都有广泛的应用。要注意摘要算法不是加密算法,不能用于加密(因为无法通过摘要反推明文),只能用于防篡改,但是它的单向计算特性决定了可以在不存储明文口令的情况下验证用户口令。

2.6 hashlib练习

  • 推荐使用 sha1

SHA1的全称是Secure Hash Algorithm(安全哈希算法) 。SHA1基于MD5,加密后的数据长度更长,它对长度小于264的输入,产生长度为160bit的散列值。比MD5多32位。

因此,比MD5更加安全,但SHA1的运算速度就比MD5要慢了。

import hashlib
sha1 = hashlib.sha1('sha1HashlibSalt666'.encode('utf-8'))
sha1.update('我是小靓仔,我最爱学习'.encode('utf-8'))
print(sha1.hexdigest())

输出:

6cc23f3cfe56761390f0261ba8bda787c1e044c1
  • 使用 sha256
import hashlib
sha256 = hashlib.sha256('sha256HashlibSalt888'.encode('utf-8'))
sha256.update('我是大老板,我有特斯拉'.encode('utf-8'))
print(sha256.hexdigest())

输出:

dcc244d1ede6e56ebd41c5330ee080bf0520e42d630fc0314505d83140165ecd
  • 使用sha512
import hashlib
sha512 = hashlib.sha512('sha512HashlibSalt000'.encode('utf-8'))
sha512.update('啥也不是,好好干'.encode('utf-8'))
print(sha512.hexdigest())

输出:

11aa49e4fa5e15547038cefa28c41739386723308f89460e8ca7acc4cd38c5ac80fb22aa37022c781114f617d09f2c95e9b1423fb7e03abeee2138ed5bf96d5b

2.7 大文件一致性校验

假如一个LOL安装包10个G,一般电脑内存肯定没那么大,要一次性读入校验,那肯定完犊子,这里就要用到我们刚才提到的 update() 方法了

  • 举例 mojito
import hashlib


def get_file_sha1(file):
    sha1 = hashlib.sha1('sha1HashHashHash666'.encode('utf-8'))
    while True:
        data = file.read(102400)
        if not data:
            break
        sha1.update(data)
    code = sha1.hexdigest()
    return code


with open(r'F:\data\周杰伦 - Mojito.flac', mode='rb') as f:
    res = get_file_sha1(f)
    print(res)

输出:

7de33e1ef04507d6859ae1439f6746a645adc97d

猜你喜欢

转载自blog.csdn.net/Sunny_Future/article/details/108372585
今日推荐