哈希算法(Hash function)又称散列算法,是一种从任何数据(文件、字符等)中创建小的数字“指纹”的方法。哈希算法只需满足把一个散列对象映射到另一个区间的需求,因此根据使用场景的不同,可将哈希算法分为加密哈希与非加密哈希。
概述
加密哈希被认为是单向函数,也就是说极难由散列函数输出的结果,回推输入的数据是什么。加密哈希函数的输入数据,通常被称为消息(message),而它的输出结果通常被称为摘要(digest)。一个理想的密码散列函数通常具有以下三个特性:
单向性:极难由一个已知的散列数值,推算出原始的消息;
唯一性:在不改动散列数值的前提下,修改消息内容是不可行的;
抗碰撞性:对于两个不同的消息,它不能给与相同的散列数值。
其中不可碰撞性是指以当前的算法与算力水平,哈希碰撞的开销超出人类可以接受的水平。以SHA-256为例,其哈希数值可能性约有1077种,而目前人类估计的宇宙原子总数约1080。虽然有概率论生日悖论问题存在,N位长度的哈希表可能发生碰撞测试次数不是2N次而是只有2N/2次,但仍然是一个巨大的数字。
常见的加密哈希函数有MD5、SHA-1、SHA-2(包含SHA-224、SHA-256、SHA-512等),虽然种类繁多,但除了生成摘要的长度、循环体内容等有一些差异外,算法的基本结构是一致的。下面以SHA-256为例,详细介绍加密哈希算法的执行步骤。
SHA-256实现原理
常量初始化
SHA-256算法中用到了8个哈希初值以及64个哈希常量,其中,8个哈希初值是对自然数前8个质数(2,3,5,7,11,13,17,19)的平方根的小数部分取前32 bit:
64个哈希常量是对自然数中前64个质数的立方根的小数部分取前32 bit,标记为k[t]:
附加长度值
SHA-256用一个64位的数据来表示原始消息的长度,而在信息处理的过程中给需要将消息分解成512bit大小的块,因此补位后的消息长度应该是512的整数倍。附加长度值分为两个步骤:
第一个bit位补1,然后都补0,直到长度满足对512取模后余数是448,如果长度已经满足对512取模后余数是448,需要填充512个bit;
附加长度为64bit的长度值。
为什么不可碰撞性对加密哈希算法如此重要?从SHA-256算法的实现步骤可以看出,加密哈希的逆向计算几乎是不可能的,暴力破解法的成本也太高,因此对加密哈希算法所谓的攻击实际是利用哈希碰撞为突破口进行数据伪造。以常见的保存用户密码为例,如果是明文存储,一旦发生数据泄露,那么所有的账户都会被盗用,因此常用下面一些方法进行Hash加密:
Hash加密:单纯对密码进行Hash加密无法保证密码的安全性,因为用户密码通常是短字符,无论采用哪种加密算法,都可以利用暴力破解或彩虹表攻击破解。
Hash加盐:在原消息上添加随机盐再进行哈希加密,并将盐与密码保存起来,以便下次登陆验证,添加随机盐增加了彩虹表破解的难度,促使攻击者放弃破解。但是如果对密码进行不安全的散列函数(MD5)计算,数据库泄露后,攻击者可以根据散列值找出碰撞的消息,不管这个消息是否与密码相同,都可以通过验证。
专用哈希函数加密:使用bcrypt等专门用来密码加密的哈希函数进行加密,这类函数通常运算时间较长,大大增加了攻击成本。
密码加密不单单是一个技术问题,对于攻击者来说,如果破解成本大于收益成本,即使攻破了加密的密码也是无意义的。而那些被证明可以产生散列碰撞的Hash函数,攻击它们的成本较低,随着算法的改进与硬件水平的提升,破解的成本也不断降低。从安全的角度考虑,应该及时更换不安全的哈希算法。
加密哈希的应用比较广泛,笔者详细地学习哈希算法的实现原理,也是为了更好地理解它们使用与被攻击的方式,而不仅仅是在编程中调一个函数库。在这之前以为密码学的知识比较晦涩难懂,实际上抛开中间的数学运算,整体的逻辑十分清晰,基本结构很容易理解。
审核编辑:符乾江
全部0条评论
快来发表一下你的评论吧 !