目录
一,什么是哈希函数?
二,哈希表(hash table)原理
三,为什么不是所有的 hash 函数都可以被用来加密?
四,哈希碰撞(hash collision)的解决方式
五,什么是好的哈希函数?
六,哈希函数的构造方法
七,为什么不能对可变的对象进行 hash 处理?
八,大型网站如何利用 hash 函数保护用户密码?
九,Python3.x 增加的随机性
十,其他 hash 算法
背景
哈希(hash)算法,原先是一种被用在资料编码中的技术,可以被用来加密要隐藏的资料,还可以被用来,比较不同文本的相似度。哈希算法属于信息摘要(Message Digest)算法。
区块链技术背后的底层原理之一,即为哈希算法。理解了哈希函数,自然就会理解区块链的挖矿模式。
全面的 hash 算法列表网址: ( http://burtleburtle.net/bob/hash/ )
一,什么是哈希函数?
哈希函数(hash function): 也称为「散列函数」或「杂凑函数」。
h = H(M)
哈希函数将任意长度的消息 M,映射成为一个长度较短且长度固定的值 H(M)。H(M)即为散列值或哈希值(hash value)。
哈希算法是一种信息摘要(Message Digest)算法。对象的 hash 值比原对象拥有更低的内存复杂度。是一种压缩映射。
根据哈希函数所建立的表为哈希表。
2
二,哈希表(hash table)原理
哈希表(hash table)|散列表,是根据关键码值进行访问的数据结构。属于键和值之间的一 一对应关系。
通常提供查找(search),插入(insert),删除(delete)等操作。跟数组一样都是一种数据结构。与字典类似,通过键值对(key-indexed)来保存数据。
目前大部分动态语言的实现中都使用了哈希表。有些语音将哈希表乘坐字典,但是哈希表跟字典并不完全相同。哈希表是弱类型的,而字典是强类型的。
哈希表查找一个元素的时间复杂度为0(1),效率极高,这也是它的一个重要优点。
键(key):操作数据的标识。
槽(bucket):用于存放数据的单元。
创建自己的哈希表
三,大型网站如何利用 hash 函数保护用户密码?
即使大型网站的数据库被攻破,也不会造成太大损失。为什么呢?这涉及到了哈希函数的两个主要特性:不可逆性与抗碰撞性。
利用哈希函数对用户密码进行加密:用户设置完密码后,会被转换成 hash 值,并保存到数据库中。如下图。
pip3 installsimhash
被保存的只是转换后的哈希值,而不是原密码。
由于哈希(hash)函数是不可逆的,只能由输入产生输出,不能由输出产生输入。系统服务器无法根据每个 hash 值逆过来推算出原密码。因此即使是后台工作人员,也无法得知用户的原密码。
用户登录的时候,输入密码,经过 hash 操作后转换成 hash 值,再与数据库中被保存的 hash 值进行对比。hash 值完全一样,则密码正确。
四,为什么不是所有的 hash 函数都可以被用来加密?
不是所有的 hash 函数都可以被用来加密,这就涉及到了哈希碰撞(hash collision)。
对于 hash 算法,一般情况下,不同的数据会生成不同的哈希值。但是如果两个不同的数据,经过 Hash 函数计算后,得到的 Hash 值一样,则为哈希碰撞(collision)。
即 f (key1) = f(key2)
哈希碰撞无法被完全避免。只能降低其发生概率。
碰撞攻击则是找到另一个跟原密码具有相同 hash 值的字符串。
为了提高安全性,只有加密哈希函数(cryptgraphic hash functions)可以被用来加密。如 SHA256, SHA512, Ripemd, WHIRLPOOL 等 。这类函数的 hash 破解异常困难,几乎不太可能出现 hash 碰撞。
五,哈希碰撞(hash collision)的解决方式
一,开放寻址法(open addressing)
二,拉链法
开放寻址法的总体设计原理为:出现哈希碰撞时,重新检测一个空闲位置,并插入。
但是这样会产生一个问题,即占用其他槽位的空间,以致于后续的插入操作更容易出现冲突。因此在利用开放寻址法的时候,装载因子最好保持在0.5以下。
装载因子 = 保存的元素数量/容量
[1]开放寻址法有:
线性探测器(Linear Probing)
二次探测法(Quadratic Probing)
随机探测法
双散列(Double hashing)
再哈希法
建立一个公共溢出区
线性探测法介绍:
线性探测法为开放寻址法最简单的一种实现。
线性探测法用来解决哈希碰撞的原理是:当某个被给定键在散列表中的对应单元已被占用时(即我们向当前哈希表写入数据时发生了冲突),会检测散列表中离冲突单元最近的空闲单元。并把该键值对写入到下一个不为空的位置。
只要哈希表未被填满,总能找到一个不发生冲突的单元。
再哈希法:
再哈希法用来解决哈希碰撞的原理是:两个值产生冲突时,通过下面算式,计算另一地址,直到不再冲突。
Hi = R * Hi(key) [i = 1,2,...k]
同样的关键字,同样的哈希函数,用不同的处理哈希碰撞的方法,得到的哈希函数值也不同。
六,什么是好的哈希函数?
哈希表的关键点就在于哈希函数的选择。
若对于关键字集合中的,任意一个关键字,经过哈希函数,映像到地址集合中,任何一个地址的概率是相等的,则为均匀散列函数(uniform hash function)。也即好的哈希函数。
这种方法通过将关键字映射到“均匀分布的随机地址”来减少冲突。
目前的哈希算法都能良好的将key进行比较均匀的分布。
七,哈希函数的构造方法:
直接定址法
除留余数法
数字分析法
折叠法
平方取中法
直接定址法:取关键字,或关键字的某个线性函数值,为哈希地址。
即:H(key) = key 或 H (key) = a * key + b
链地址法:将所有相似关键词的记录存储在同一线性链表中。
八,为什么不能对可变的对象进行 hash 处理?
首先要了解两个概念,可哈希性(hashable)与不可哈希性(unhashable)。
可哈希性(hashable):可哈希的数据类型为不可变的数据结构(如字符串 srt,元组 tuple,对象集 objects 等)。这种数据被称为可哈希性。
不可哈希性:不可哈希的数据类型,为可变的数据结构(如字典 dict,列表 list 和集合 set 等)。
如果对可变的对象进行哈希处理,则每次对象更新时,都需要更新哈希表。这样我们则需要将对象移至不同的数据集,这种操作会使花费过大。
因此设定不能对可变的对象进行 hash 处理。
九,Python3.x 增加的随机性
Python3.x添加了 hash 算法的随机性,以提高安全性,因此对于每个新的 python 调用,同样的数据源生成的结果都将不同。 下面为算法实例。
哈希方法有(MD5, SHA1, SHA256 与 SHA512 等)。常用的有 SH256 与 SHA512,即上文提到的加密哈希函数(cryptgraphic hash functions)。MD5 与 SHA1 不再常用。
MDH5 (不常用)
SHA1 (不常用)
SHA256 (常用)
SHA512 (常用)
十,其他 hash 算法**
1. simhash 算法:
这是一种局部敏感的 hash 算法,它产生的签名在一定程度上可以表征原内容的相似度。
可以被用来比较文本的相似度。
如下代码安装 simhash 模块。
pip3 installsimhash
2. Imagehash 算法:
Imagehash 算法为感知哈希算法(perceptual Hash Algorithm)。
常被用来检测图像和视频的差异。
如下代码安装 Imagehash 模块。
pip3 install simhash
例如:可以通过比较两张图片的 hash 值来判断相似度。下面为比较两张相似图片的代码示例。
输出为:
hash(image1) =00fd43c3**f**fffffff
hash(image2) =00fd43c3f**e**ffffff
从输出结果可以看到两张图片的 hash 值非常相似。相似的图片可以生成相似的哈希值是 Imagehash 的特点。
后续再详细更新哈希碰撞的解决方式